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
ADR-0011 requires TLS on the consumer stream but leaves the provider open. From Exposure requirement:
Any non-loopback bind (stream_addr / stream_grpc_addr beyond 127.0.0.1) requires TLS, native or a trusted TLS-terminating proxy.
and again in Consequences, listing "TLS (proxy or native)" among the mitigations. Since the choice was left open, it may be worth closing it before the stream stack (#57, #65, #68) reaches main, because neither option has been taken yet.
Current state
No TLS is served on either transport. WS is httpSrv.ListenAndServe(); the gRPC server added in feat: add consumer block-stream gRPC transport #68 is grpc.NewServer() with no credentials. There is no cert or key config in pkg/config.
Nothing enforces the requirement. validateStreamListener covers only the auth rule from the sentence that follows it in the ADR (!requireAuth && !isLoopbackHost), so a non-loopback bind without TLS passes startup validation.
The defaults are 0.0.0.0:9600 and 0.0.0.0:9601, exactly the non-loopback case the ADR says requires TLS. OPT_STREAM_ENABLE=true alone therefore yields a plaintext consumer stream on all interfaces, with the consumer JWT crossing the wire in the clear.
1. Native TLS in the gateway. Config for cert and key paths, ServeTLS for WS, credentials.NewTLS for gRPC. Self-contained and correct by default, at the cost of certificate lifecycle in the gateway (renewal, reload on rotation, one more failure mode in the container).
2. Proxy-only, with the requirement made unavoidable. Both listeners stay plaintext, but the unsafe configuration stops starting silently. Since the process cannot detect an upstream proxy, the honest form is an explicit acknowledgement, for example OPT_STREAM_INSECURE_NO_TLS defaulting to false and required for a non-loopback bind. Certificate handling stays out of the gateway while plaintext becomes a deliberate choice rather than the default. Framed as opt-in-to-insecure rather than opt-in-to-TLS on purpose: an affirmative "TLS is present" flag would be equally unverifiable, and a false default is the safer shape.
3. Operator responsibility, documented only. Status quo, with the Helm guide and config reference stating that a TLS-terminating proxy is required in front of stream_addr and stream_grpc_addr. Cheapest and consistent with how the ADR frames it, but a misconfiguration stays silent and looks like a working deployment.
These are not exclusive. 2 and 3 together are probably the smallest change that closes the gap, with 1 available later if operators want native termination.
Why it matters more here than for the gateway's own credentials
A consumer JWT is a bearer credential handed to a third party, and the gateway verifies locally and never introspects, so the token's TTL is the entire revocation window. optimum-auth sets that to 1h by default for stream tokens specifically, deliberately shorter than the 6h gateway token, on the grounds that a credential given to a third party needs a tighter window. Leaking one in transit is correspondingly more costly.
Ask
Which of the three is intended. Whichever it is, the gap worth closing before the stack merges is that nothing currently distinguishes "correctly fronted by a proxy" from "plaintext on 0.0.0.0".
Context
ADR-0011 requires TLS on the consumer stream but leaves the provider open. From Exposure requirement:
and again in Consequences, listing "TLS (proxy or native)" among the mitigations. Since the choice was left open, it may be worth closing it before the stream stack (#57, #65, #68) reaches main, because neither option has been taken yet.
Current state
httpSrv.ListenAndServe(); the gRPC server added in feat: add consumer block-stream gRPC transport #68 isgrpc.NewServer()with no credentials. There is no cert or key config inpkg/config.validateStreamListenercovers only the auth rule from the sentence that follows it in the ADR (!requireAuth && !isLoopbackHost), so a non-loopback bind without TLS passes startup validation.0.0.0.0:9600and0.0.0.0:9601, exactly the non-loopback case the ADR says requires TLS.OPT_STREAM_ENABLE=truealone therefore yields a plaintext consumer stream on all interfaces, with the consumer JWT crossing the wire in the clear.grpcurl -plaintext.Options
1. Native TLS in the gateway. Config for cert and key paths,
ServeTLSfor WS,credentials.NewTLSfor gRPC. Self-contained and correct by default, at the cost of certificate lifecycle in the gateway (renewal, reload on rotation, one more failure mode in the container).2. Proxy-only, with the requirement made unavoidable. Both listeners stay plaintext, but the unsafe configuration stops starting silently. Since the process cannot detect an upstream proxy, the honest form is an explicit acknowledgement, for example
OPT_STREAM_INSECURE_NO_TLSdefaulting to false and required for a non-loopback bind. Certificate handling stays out of the gateway while plaintext becomes a deliberate choice rather than the default. Framed as opt-in-to-insecure rather than opt-in-to-TLS on purpose: an affirmative "TLS is present" flag would be equally unverifiable, and a false default is the safer shape.3. Operator responsibility, documented only. Status quo, with the Helm guide and config reference stating that a TLS-terminating proxy is required in front of
stream_addrandstream_grpc_addr. Cheapest and consistent with how the ADR frames it, but a misconfiguration stays silent and looks like a working deployment.These are not exclusive. 2 and 3 together are probably the smallest change that closes the gap, with 1 available later if operators want native termination.
Why it matters more here than for the gateway's own credentials
A consumer JWT is a bearer credential handed to a third party, and the gateway verifies locally and never introspects, so the token's TTL is the entire revocation window. optimum-auth sets that to 1h by default for stream tokens specifically, deliberately shorter than the 6h gateway token, on the grounds that a credential given to a third party needs a tighter window. Leaking one in transit is correspondingly more costly.
Ask
Which of the three is intended. Whichever it is, the gap worth closing before the stack merges is that nothing currently distinguishes "correctly fronted by a proxy" from "plaintext on
0.0.0.0".