From 9677a9f71777d75f3def0b0cb238050a30ec6a67 Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Wed, 29 Jul 2015 01:26:10 -0800 Subject: Fixes mm-1355 adds rate limiting apis --- api/server.go | 37 +++++++++++++++++++++++++++++++++++-- api/user.go | 14 ++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) (limited to 'api') diff --git a/api/server.go b/api/server.go index 3163f79f5..3273e766c 100644 --- a/api/server.go +++ b/api/server.go @@ -9,7 +9,10 @@ import ( "github.com/gorilla/mux" "github.com/mattermost/platform/store" "github.com/mattermost/platform/utils" + "github.com/throttled/throttled" + throttledStore "github.com/throttled/throttled/store" "net/http" + "strings" "time" ) @@ -35,10 +38,40 @@ func NewServer() { func StartServer() { l4g.Info("Starting Server...") - l4g.Info("Server is listening on " + utils.Cfg.ServiceSettings.Port) + + var handler http.Handler = Srv.Router + + if utils.Cfg.RateLimitSettings.UseRateLimiter { + l4g.Info("RateLimiter is enabled") + + vary := throttled.VaryBy{} + + if utils.Cfg.RateLimitSettings.VaryByRemoteAddr { + vary.RemoteAddr = true + } + + if len(utils.Cfg.RateLimitSettings.VaryByHeader) > 0 { + vary.Headers = strings.Fields(utils.Cfg.RateLimitSettings.VaryByHeader) + + if utils.Cfg.RateLimitSettings.VaryByRemoteAddr { + l4g.Warn("RateLimitSettings not configured properly using VaryByHeader and disabling VaryByRemoteAddr") + vary.RemoteAddr = false + } + } + + th := throttled.RateLimit(throttled.PerSec(utils.Cfg.RateLimitSettings.PerSec), &vary, throttledStore.NewMemStore(utils.Cfg.RateLimitSettings.MemoryStoreSize)) + + th.DeniedHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + l4g.Error("%v: code=429 ip=%v", r.URL.Path, GetIpAddress(r)) + throttled.DefaultDeniedHandler.ServeHTTP(w, r) + }) + + handler = th.Throttle(Srv.Router) + } + go func() { - err := Srv.Server.ListenAndServe(":"+utils.Cfg.ServiceSettings.Port, Srv.Router) + err := Srv.Server.ListenAndServe(":"+utils.Cfg.ServiceSettings.Port, handler) if err != nil { l4g.Critical("Error starting server, err:%v", err) time.Sleep(time.Second) diff --git a/api/user.go b/api/user.go index e1d5e83dd..94b4eca18 100644 --- a/api/user.go +++ b/api/user.go @@ -282,12 +282,26 @@ func LoginByEmail(c *Context, w http.ResponseWriter, r *http.Request, email, nam } func checkUserPassword(c *Context, user *model.User, password string) bool { + + if user.FailedAttempts >= utils.Cfg.ServiceSettings.AllowedLoginAttempts { + c.LogAuditWithUserId(user.Id, "fail") + c.Err = model.NewAppError("checkUserPassword", "Your account is locked because of too many failed password attempts. Please reset your password.", "user_id="+user.Id) + c.Err.StatusCode = http.StatusForbidden + return false + } + if !model.ComparePassword(user.Password, password) { c.LogAuditWithUserId(user.Id, "fail") c.Err = model.NewAppError("checkUserPassword", "Login failed because of invalid password", "user_id="+user.Id) c.Err.StatusCode = http.StatusForbidden + + if result := <-Srv.Store.User().UpdateFailedPasswordAttempts(user.Id, user.FailedAttempts+1); result.Err != nil { + c.LogError(result.Err) + } + return false } + return true } -- cgit v1.2.3-1-g7c22 From b4877c5d36569dae486a1e53f39c35d346e3d3d2 Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Thu, 30 Jul 2015 00:46:17 -0800 Subject: Fixing code review feedback --- api/user.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'api') diff --git a/api/user.go b/api/user.go index 94b4eca18..3c9ae5718 100644 --- a/api/user.go +++ b/api/user.go @@ -300,9 +300,14 @@ func checkUserPassword(c *Context, user *model.User, password string) bool { } return false + } else { + if result := <-Srv.Store.User().UpdateFailedPasswordAttempts(user.Id, 0); result.Err != nil { + c.LogError(result.Err) + } + + return true } - return true } // User MUST be validated before calling Login -- cgit v1.2.3-1-g7c22