fix(api): cap concurrent SSE streams per principal

Console and build-log relays hold a Server-Sent Event connection open for the
life of a client's attachment; a stalled reader pins the relay goroutine plus
its upstream kube-apiserver follow. Without a bound, one authenticated
principal could open these repeatedly and accumulate leaked control-plane
connections.

Add a per-principal stream cap (streamLimiter) enforced before either relay
opens its follow stream, returning 429 too_many_streams past the limit.
cmd/felis wires it to 16; zero disables it, matching the "zero disables"
idiom of the other levers.

This bounds the blast radius of the stalled-stream leak; it does not close the
leak itself -- the per-write deadline that severs a stalled stream is a
separate change.
This commit is contained in:
flyemoji committed 2026-07-01 22:04:19 +09:00
1 parent c6c0772a7a
commit 3c1d64749f
5 files changed
+175

No files matched your search

+4
View File
@@ -173,6 +173,10 @@ func cmdAPI(args []string, stdout, stderr io.Writer) int {
RootDomain: cfg.Server.RootDomain,
WakeCooldown: 30 * time.Second,
MaxConcurrentLogins: loginBcryptCap,
// Bound concurrent console/build-log SSE streams per principal. Generous enough
// for legitimate multi-tab / multi-server watching, while capping how many
// upstream follow connections a single caller can tie up if their streams stall.
MaxStreamsPerPrincipal: 16,
}
fmt.Fprintln(stderr, "felis api: external face fails closed (Access JWKS key function not configured)")