// Copyright 2023 wanderer // SPDX-License-Identifier: AGPL-3.0-only package app import ( "encoding/hex" "net/http" "regexp" "github.com/gorilla/sessions" "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "golang.org/x/exp/slog" "golang.org/x/time/rate" ) // SetServerSettings sets up the main Echo instance and panics on err. func (a *App) SetServerSettings() { e := a.E() e.HideBanner = true if a.setting.DefaultLoggerSkipAssets() { re := regexp.MustCompile("^/(assets|static)(.*)|favicon.ico") lC := middleware.DefaultLoggerConfig lC.Skipper = func(c echo.Context) bool { r := c.Request().URL.Path slog.Debug("logger skipper", "path", r) return re.MatchString(r) } e.Use(middleware.LoggerWithConfig(lC)) } else { e.Use(middleware.Logger()) } // e.Use(middleware.LoggerWithConfig( // middleware.LoggerConfig{ // Format: `{"time":"${time_rfc3339_nano}","id":"${id}","remote_ip":"${remote_ip}",` + // `"host":"${host}","method":"${method}","uri":"${uri}","user_agent":"${user_agent}",` + // `"status":${status},"error":"${error}","latency":${latency},"latency_human":"${latency_human}"` + // `,"bytes_in":${bytes_in},"bytes_out":${bytes_out}}` + "\n", // CustomTimeFormat: "2006-01-02 15:04:05.00000", // }, // )) // logger := zerolog.New(os.Stdout) // e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ // LogURI: true, // LogStatus: true, // LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { // logger.Info(). // Str("URI", v.URI). // Int("status", v.Status). // Msg("request") // // return nil // }, // })) // TODO: make this configurable. e.Server.WriteTimeout = a.setting.DefaultServerWriteTimeout() e.Server.ReadHeaderTimeout = a.setting.DefaultServerReadHeaderTimeout() if a.setting.HTTPRateLimitEnabled() { limit := rate.Limit(a.setting.HTTPRateLimit()) e.Use(middleware.RateLimiter( middleware.NewRateLimiterMemoryStore(limit), )) } // TODO: add check for prometheus config setting. // if true { // // import "github.com/labstack/echo-contrib/prometheus" // p := prometheus.NewPrometheus("echo", nil) // p.Use(e) // } e.Use(middleware.Recover()) var ( store *sessions.CookieStore authSecret []byte encrSecret []byte ) if a.setting.SessionAuthIsHex() { b, err := hex.DecodeString(a.setting.SessionCookieAuthSecret()) if err != nil { panic(err) } authSecret = b } if a.setting.SessionEncrIsHex() { b, err := hex.DecodeString(a.setting.SessionCookieEncrSecret()) if err != nil { panic(err) } encrSecret = b } switch { case authSecret != nil && encrSecret != nil: store = sessions.NewCookieStore( authSecret, encrSecret, ) case authSecret != nil && encrSecret == nil: store = sessions.NewCookieStore( authSecret, []byte(a.setting.SessionCookieEncrSecret()), ) case authSecret == nil && encrSecret != nil: store = sessions.NewCookieStore( []byte(a.setting.SessionCookieAuthSecret()), encrSecret, ) case authSecret == nil && encrSecret == nil: store = sessions.NewCookieStore( []byte(a.setting.SessionCookieAuthSecret()), []byte(a.setting.SessionCookieEncrSecret()), ) } store.Options.Path = "/" store.Options.Domain = a.setting.HTTPDomain() store.Options.HttpOnly = true store.Options.SameSite = http.SameSiteStrictMode store.Options.Secure = a.setting.HTTPSecure() store.Options.MaxAge = a.setting.SessionMaxAge() if a.setting.HTTPSecure() { // https://www.sjoerdlangkemper.nl/2017/02/09/cookie-prefixes/ // https://scotthelme.co.uk/tough-cookies/ // https://check-your-website.server-daten.de/prefix-cookies.html store.Options.Domain = "__Host-" + store.Options.Domain } e.Use(session.Middleware(store)) e.Use(middleware.Secure()) if a.setting.HTTPGzipEnabled() { e.Use(middleware.GzipWithConfig(middleware.GzipConfig{ Level: a.setting.HTTPGzipLevel(), })) } } func (a *App) csrfConfig() echo.MiddlewareFunc { csrfCookieName := "pcmt_csrf" if a.setting.HTTPSecure() { // https://www.sjoerdlangkemper.nl/2017/02/09/cookie-prefixes/ // https://scotthelme.co.uk/tough-cookies/ // https://check-your-website.server-daten.de/prefix-cookies.html csrfCookieName = "__Host-" + csrfCookieName } return middleware.CSRFWithConfig(middleware.CSRFConfig{ TokenLookup: "cookie:" + csrfCookieName + ",form:csrf,header:" + echo.HeaderXCSRFToken, CookieName: csrfCookieName, ContextKey: "csrf", // CookieDomain: "localhost", CookieSecure: a.setting.HTTPSecure(), CookieHTTPOnly: true, CookieSameSite: http.SameSiteStrictMode, CookieMaxAge: a.setting.SessionMaxAge(), CookiePath: "/", }, ) }