diff --git a/docs/api/fiber.md b/docs/api/fiber.md index a34a5d944c..17cf3896b9 100644 --- a/docs/api/fiber.md +++ b/docs/api/fiber.md @@ -116,7 +116,8 @@ app.Listen(":8080", fiber.ListenConfig{ | OnShutdownError | `func(err error)` | Allows to customize error behavior when gracefully shutting down the server by given signal. Prints error with `log.Fatalf()` | `nil` | | OnShutdownSuccess | `func()` | Allows customizing success behavior when gracefully shutting down the server by given signal. | `nil` | | TLSConfigFunc | `func(tlsConfig *tls.Config)` | Allows customizing `tls.Config` as you want. | `nil` | -| AutoCertManager | `func(tlsConfig *tls.Config)` | Manages TLS certificates automatically using the ACME protocol. Enables integration with Let's Encrypt or other ACME-compatible providers. | `nil` | +| AutoCertManager | `*autocert.Manager` | Manages TLS certificates automatically using the ACME protocol. Enables integration with Let's Encrypt or other ACME-compatible providers. | `nil` | +| TLSMinVersion | `uint16` | Allows customizing the TLS minimum version. | `tls.VersionTLS12` | ### Listen diff --git a/docs/whats_new.md b/docs/whats_new.md index bfc6f25c29..eadc1afa4a 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -130,6 +130,14 @@ In this example, a custom context `CustomCtx` is created with an additional meth +### Configurable TLS Minimum Version + +We have added support for configuring the TLS minimum version. This field allows you to set the TLS minimum version for TLSAutoCert and the server listener. + +```go +app.Listen(":444", fiber.ListenConfig{TLSMinVersion: tls.VersionTLS12}) +``` + #### TLS AutoCert support (ACME / Let's Encrypt) We have added native support for automatic certificates management from Let's Encrypt and any other ACME-based providers. diff --git a/go.mod b/go.mod index af46fd9f3a..ebdc9080e8 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/stretchr/testify v1.10.0 github.com/tinylib/msgp v1.2.5 github.com/valyala/bytebufferpool v1.0.0 - github.com/valyala/fasthttp v1.57.0 + github.com/valyala/fasthttp v1.58.0 golang.org/x/crypto v0.31.0 ) @@ -24,7 +24,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/net v0.30.0 // indirect + golang.org/x/net v0.31.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index b6af6a6caa..5b8204d1ee 100644 --- a/go.sum +++ b/go.sum @@ -27,8 +27,8 @@ github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po= github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.57.0 h1:Xw8SjWGEP/+wAAgyy5XTvgrWlOD1+TxbbvNADYCm1Tg= -github.com/valyala/fasthttp v1.57.0/go.mod h1:h6ZBaPRlzpZ6O3H5t2gEk1Qi33+TmLvfwgLLp0t9CpE= +github.com/valyala/fasthttp v1.58.0 h1:GGB2dWxSbEprU9j0iMJHgdKYJVDyjrOwF9RE59PbRuE= +github.com/valyala/fasthttp v1.58.0/go.mod h1:SYXvHHaFp7QZHGKSHmoMipInhrI5StHrhDTYVEjK/Kw= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= @@ -37,8 +37,8 @@ github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZ github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= diff --git a/listen.go b/listen.go index e0c5536968..793d36d2a1 100644 --- a/listen.go +++ b/listen.go @@ -108,6 +108,12 @@ type ListenConfig struct { // Default: 10 * time.Second ShutdownTimeout time.Duration `json:"shutdown_timeout"` + // TLSMinVersion allows to set TLS minimum version. + // + // Default: tls.VersionTLS12 + // WARNING: TLS1.0 and TLS1.1 versions are not supported. + TLSMinVersion uint16 `json:"tls_min_version"` + // When set to true, it will not print out the «Fiber» ASCII art and listening address. // // Default: false @@ -128,6 +134,7 @@ type ListenConfig struct { func listenConfigDefault(config ...ListenConfig) ListenConfig { if len(config) < 1 { return ListenConfig{ + TLSMinVersion: tls.VersionTLS12, ListenerNetwork: NetworkTCP4, OnShutdownError: func(err error) { log.Fatalf("shutdown: %v", err) //nolint:revive // It's an option @@ -147,6 +154,14 @@ func listenConfigDefault(config ...ListenConfig) ListenConfig { } } + if cfg.TLSMinVersion == 0 { + cfg.TLSMinVersion = tls.VersionTLS12 + } + + if cfg.TLSMinVersion != tls.VersionTLS12 && cfg.TLSMinVersion != tls.VersionTLS13 { + panic("unsupported TLS version, please use tls.VersionTLS12 or tls.VersionTLS13") + } + return cfg } @@ -168,8 +183,8 @@ func (app *App) Listen(addr string, config ...ListenConfig) error { } tlsHandler := &TLSHandler{} - tlsConfig = &tls.Config{ - MinVersion: tls.VersionTLS12, + tlsConfig = &tls.Config{ //nolint:gosec // This is a user input + MinVersion: cfg.TLSMinVersion, Certificates: []tls.Certificate{ cert, }, @@ -192,8 +207,8 @@ func (app *App) Listen(addr string, config ...ListenConfig) error { // Attach the tlsHandler to the config app.SetTLSHandler(tlsHandler) } else if cfg.AutoCertManager != nil { - tlsConfig = &tls.Config{ - MinVersion: tls.VersionTLS12, + tlsConfig = &tls.Config{ //nolint:gosec // This is a user input + MinVersion: cfg.TLSMinVersion, GetCertificate: cfg.AutoCertManager.GetCertificate, NextProtos: []string{"http/1.1", "acme-tls/1"}, } diff --git a/listen_test.go b/listen_test.go index 123cf2b3b8..032f7d32c4 100644 --- a/listen_test.go +++ b/listen_test.go @@ -236,6 +236,43 @@ func Test_Listen_Prefork(t *testing.T) { require.NoError(t, app.Listen(":99999", ListenConfig{DisableStartupMessage: true, EnablePrefork: true})) } +// go test -run Test_Listen_TLSMinVersion +func Test_Listen_TLSMinVersion(t *testing.T) { + testPreforkMaster = true + + app := New() + + // Invalid TLSMinVersion + require.Panics(t, func() { + _ = app.Listen(":443", ListenConfig{TLSMinVersion: tls.VersionTLS10}) //nolint:errcheck // ignore error + }) + require.Panics(t, func() { + _ = app.Listen(":443", ListenConfig{TLSMinVersion: tls.VersionTLS11}) //nolint:errcheck // ignore error + }) + + // Prefork + require.Panics(t, func() { + _ = app.Listen(":443", ListenConfig{DisableStartupMessage: true, EnablePrefork: true, TLSMinVersion: tls.VersionTLS10}) //nolint:errcheck // ignore error + }) + require.Panics(t, func() { + _ = app.Listen(":443", ListenConfig{DisableStartupMessage: true, EnablePrefork: true, TLSMinVersion: tls.VersionTLS11}) //nolint:errcheck // ignore error + }) + + // Valid TLSMinVersion + go func() { + time.Sleep(1000 * time.Millisecond) + assert.NoError(t, app.Shutdown()) + }() + require.NoError(t, app.Listen(":0", ListenConfig{TLSMinVersion: tls.VersionTLS13})) + + // Valid TLSMinVersion with Prefork + go func() { + time.Sleep(1000 * time.Millisecond) + assert.NoError(t, app.Shutdown()) + }() + require.NoError(t, app.Listen(":99999", ListenConfig{DisableStartupMessage: true, EnablePrefork: true, TLSMinVersion: tls.VersionTLS13})) +} + // go test -run Test_Listen_TLS func Test_Listen_TLS(t *testing.T) { app := New() diff --git a/middleware/session/middleware_test.go b/middleware/session/middleware_test.go index 579d61c44c..9cbb8cd53b 100644 --- a/middleware/session/middleware_test.go +++ b/middleware/session/middleware_test.go @@ -166,25 +166,9 @@ func Test_Session_Middleware(t *testing.T) { h(ctx) require.Equal(t, fiber.StatusOK, ctx.Response.StatusCode()) - // Verify the session cookie is set to expire + // Verify the session cookie has expired setCookieHeader := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) - require.Contains(t, setCookieHeader, "expires=") - cookieParts := strings.Split(setCookieHeader, ";") - expired := false - for _, part := range cookieParts { - if strings.Contains(part, "expires=") { - part = strings.TrimSpace(part) - expiryDateStr := strings.TrimPrefix(part, "expires=") - // Correctly parse the date with "GMT" timezone - expiryDate, err := time.Parse(time.RFC1123, strings.TrimSpace(expiryDateStr)) - require.NoError(t, err) - if expiryDate.Before(time.Now()) { - expired = true - break - } - } - } - require.True(t, expired, "Session cookie should be expired") + require.Contains(t, setCookieHeader, "max-age=0") // Sleep so that the session expires time.Sleep(1 * time.Second)