eleven / messenger Public

server: ReadHeaderTimeout on the instance + tunnel-gateway listeners (#903)

Slowloris hardening (Aikido SAST G112): the router, cloud, and controld
listeners already cap header reads at 10s; the instance server (which a
standalone self-host binds straight to public TCP) and the tunnel
gateway missed it. ReadHeaderTimeout only bounds header parsing, so
WebSockets and other long-lived connections are unaffected.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 3d82cac833fa3a228b1d66e1414e080e8ef19e83 parent 4c4bd3e
2 files changed, +5 −2
  • main.go +4 −1
  • trycmd.go +1 −1
diff --git a/main.go b/main.go
index d90687e..97470c4 100644
--- a/main.go
+++ b/main.go
@@ -326,7 +326,10 @@ func main() {
}
log.Printf("lchat listening on %s (db: %s)", where, *dbPath)
- srv := &http.Server{Handler: s.handler()}
+ // ReadHeaderTimeout caps how long a client may dribble request headers
+ // (Slowloris): standalone self-hosts bind this listener straight to public
+ // TCP. Same 10s the router/cloud/controld listeners use.
+ srv := &http.Server{Handler: s.handler(), ReadHeaderTimeout: 10 * time.Second}
go func() {
if err := srv.Serve(ln); err != nil && err != http.ErrServerClosed {
log.Fatalf("serve: %v", err)
diff --git a/trycmd.go b/trycmd.go
index d24b274..e8bd45d 100644
--- a/trycmd.go
+++ b/trycmd.go
@@ -254,7 +254,7 @@ func runTunnelCmd(args []string) {
}
_ = os.Chmod(*socket, 0o660)
fmt.Printf("tunnel gateway: %s (control %s, minting try-*.%s)\n", *socket, *host, *domain)
- srv := &http.Server{Handler: gw}
+ srv := &http.Server{Handler: gw, ReadHeaderTimeout: 10 * time.Second}
if err := srv.Serve(ln); err != nil {
die("tunnel serve: %v", err)
}