From 5792be71fa29d552ee962d4e9d2414fc4d7d5d12 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Mon, 30 May 2022 06:24:51 -0700 Subject: [PATCH] fix: add timeouts to avoid goroutine leaks in net/http (#14995) Following code can reproduce an unending go-routine buildup, while keeping connections established due to lack of client not closing the connections. https://gist.github.com/harshavardhana/2d00e6f909054d2d2524c71485ad02e1 Without this PR all MinIO deployments can be put into denial of service attacks, causing entire service to be unavailable. We bring in two timeouts at this stage to control such go-routine build ups, new change - IdleTimeout (to kill off idle connections) - ReadHeaderTimeout (to kill off connections that are too slow) This new change also brings two hidden options to make any additional relevant changes if desired in some setups. --- cmd/server-main.go | 16 ++++++++++++++++ internal/http/server.go | 22 ++++++++++++++++++++-- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/cmd/server-main.go b/cmd/server-main.go index 2c5bf2057..a0cb5872e 100644 --- a/cmd/server-main.go +++ b/cmd/server-main.go @@ -73,6 +73,20 @@ var ServerFlags = []cli.Flag{ EnvVar: "MINIO_SHUTDOWN_TIMEOUT", Hidden: true, }, + cli.DurationFlag{ + Name: "idle-timeout", + Value: xhttp.DefaultIdleTimeout, + Usage: "idle timeout is the maximum amount of time to wait for the next request when keep-alives are enabled", + EnvVar: "MINIO_IDLE_TIMEOUT", + Hidden: true, + }, + cli.DurationFlag{ + Name: "read-header-timeout", + Value: xhttp.DefaultReadHeaderTimeout, + Usage: "read header timeout is the amount of time allowed to read request headers", + EnvVar: "MINIO_READ_HEADER_TIMEOUT", + Hidden: true, + }, } var serverCmd = cli.Command{ @@ -486,6 +500,8 @@ func serverMain(ctx *cli.Context) { UseHandler(setCriticalErrorHandler(corsHandler(handler))). UseTLSConfig(newTLSConfig(getCert)). UseShutdownTimeout(ctx.Duration("shutdown-timeout")). + UseIdleTimeout(ctx.Duration("idle-timeout")). + UseReadHeaderTimeout(ctx.Duration("read-header-timeout")). UseBaseContext(GlobalContext). UseCustomLogger(log.New(ioutil.Discard, "", 0)) // Turn-off random logging by Go stdlib diff --git a/internal/http/server.go b/internal/http/server.go index 078f330ee..386c774f8 100644 --- a/internal/http/server.go +++ b/internal/http/server.go @@ -47,6 +47,12 @@ const ( // DefaultShutdownTimeout - default shutdown timeout to gracefully shutdown server. DefaultShutdownTimeout = 5 * time.Second + // DefaultIdleTimeout for idle inactive connections + DefaultIdleTimeout = 30 * time.Second + + // DefaultReadHeaderTimeout for very slow inactive connections + DefaultReadHeaderTimeout = 30 * time.Second + // DefaultMaxHeaderBytes - default maximum HTTP header size in bytes. DefaultMaxHeaderBytes = 1 * humanize.MiByte ) @@ -169,6 +175,18 @@ func (srv *Server) UseShutdownTimeout(d time.Duration) *Server { return srv } +// UseIdleTimeout configure idle connection timeout +func (srv *Server) UseIdleTimeout(d time.Duration) *Server { + srv.IdleTimeout = d + return srv +} + +// UseReadHeaderTimeout configure read header timeout +func (srv *Server) UseReadHeaderTimeout(d time.Duration) *Server { + srv.ReadHeaderTimeout = d + return srv +} + // UseHandler configure final handler for this HTTP *Server func (srv *Server) UseHandler(h http.Handler) *Server { srv.Handler = h @@ -206,8 +224,8 @@ func NewServer(addrs []string) *Server { } // SetMinIOVersion -- MinIO version from the main package is set here -func SetMinIOVersion(minioVer string) { - GlobalMinIOVersion = minioVer +func SetMinIOVersion(version string) { + GlobalMinIOVersion = version } // SetDeploymentID -- Deployment Id from the main package is set here