From f680b6e8421c9f8a89d68ee9cbd0f18a16800257 Mon Sep 17 00:00:00 2001 From: hellerve Date: Fri, 25 Nov 2016 15:50:59 +0100 Subject: [PATCH 1/6] added experimental rate limit --- bird/bird.go | 27 +++++++++++++++++++++++++++ birdwatcher.go | 1 + 2 files changed, 28 insertions(+) diff --git a/bird/bird.go b/bird/bird.go index b737344..af0dc60 100644 --- a/bird/bird.go +++ b/bird/bird.go @@ -9,6 +9,9 @@ import ( var BirdCmd string +var rateLimit = 0 +var MAX_RATE = 5 + var Cache = struct { sync.RWMutex m map[string]Parsed @@ -34,11 +37,35 @@ func Run(args string) ([]byte, error) { return exec.Command(BirdCmd, argsList...).Output() } +func InstallRateLimitReset() { + go func() { + c := time.Tick(time.Second) + + for _ = range c { + rateLimit = 0 + } + }() +} + +func checkRateLimit() bool { + if rateLimit > MAX_RATE { + return false + } + + rateLimit += 1 + + return true +} + func RunAndParse(cmd string, parser func([]byte) Parsed) (Parsed, bool) { if val, ok := fromCache(cmd); ok { return val, true } + if !checkRateLimit() { + return nil, false + } + out, err := Run(cmd) if err != nil { diff --git a/birdwatcher.go b/birdwatcher.go index df3197e..8e8b0f4 100644 --- a/birdwatcher.go +++ b/birdwatcher.go @@ -38,6 +38,7 @@ func main() { flag.Parse() bird.BirdCmd = *birdc + bird.InstallRateLimitReset() r := makeRouter() From d9ad314d1670eab0729847d380d984a23ad9117c Mon Sep 17 00:00:00 2001 From: hellerve Date: Fri, 9 Dec 2016 12:04:08 +0100 Subject: [PATCH 2/6] send too many requests on rate limit reached --- endpoints/endpoint.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/endpoints/endpoint.go b/endpoints/endpoint.go index e27ee35..73c078f 100644 --- a/endpoints/endpoint.go +++ b/endpoints/endpoint.go @@ -15,6 +15,10 @@ func Endpoint(wrapped func(httprouter.Params) (bird.Parsed, bool)) httprouter.Ha res := make(map[string]interface{}) ret, from_cache := wrapped(ps) + if ret == nil { + w.WriteHeader(http.StatusTooManyRequests) + return + } res["api"] = GetApiInfo(from_cache) for k, v := range ret { From c6730945dc16a404cb6c8886ea2f5e96001df6f7 Mon Sep 17 00:00:00 2001 From: hellerve Date: Tue, 13 Dec 2016 10:49:18 +0100 Subject: [PATCH 3/6] made ratelimit configurable --- bird/bird.go | 14 ++++++++------ bird/config.go | 6 ++++++ config.go | 7 ++++--- etc/ecix/birdwatcher.conf | 6 ++++-- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/bird/bird.go b/bird/bird.go index 3feeeb3..399146c 100644 --- a/bird/bird.go +++ b/bird/bird.go @@ -9,9 +9,7 @@ import ( var ClientConf BirdConfig var StatusConf StatusConfig - -var rateLimit = 0 -var MAX_RATE = 5 +var rateLimitConf RateLimitConfig var Cache = struct { sync.RWMutex @@ -43,17 +41,21 @@ func InstallRateLimitReset() { c := time.Tick(time.Second) for _ = range c { - rateLimit = 0 + rateLimitConf.Reqs = 0 } }() } func checkRateLimit() bool { - if rateLimit > MAX_RATE { + if !rateLimitConf.Enabled { + return true + } + + if rateLimitConf.Reqs > rateLimitConf.Max { return false } - rateLimit += 1 + rateLimitConf.Reqs += 1 return true } diff --git a/bird/config.go b/bird/config.go index b5c6788..89bf0d8 100644 --- a/bird/config.go +++ b/bird/config.go @@ -14,3 +14,9 @@ type BirdConfig struct { ConfigFilename string `toml:"config"` BirdCmd string `toml:"birdc"` } + +type RateLimitConfig struct { + Reqs int `toml:"requests_per_minute"` + Max int `toml:"requests_per_minute"` + Enabled bool +} diff --git a/config.go b/config.go index 6127f0f..82660a9 100644 --- a/config.go +++ b/config.go @@ -15,9 +15,10 @@ import ( type Config struct { Server endpoints.ServerConfig - Status bird.StatusConfig - Bird bird.BirdConfig - Bird6 bird.BirdConfig + Ratelimit bird.RateLimitConfig + Status bird.StatusConfig + Bird bird.BirdConfig + Bird6 bird.BirdConfig } // Try to load configfiles as specified in the files diff --git a/etc/ecix/birdwatcher.conf b/etc/ecix/birdwatcher.conf index 93cc840..5a5e20c 100644 --- a/etc/ecix/birdwatcher.conf +++ b/etc/ecix/birdwatcher.conf @@ -1,5 +1,3 @@ - - # # Birdwatcher Configuration # @@ -36,6 +34,10 @@ reconfig_timestamp_match = "# Created: (.*)" # Remove fields e.g. last_reboot filter_fields = [] +[ratelimit] +enabled = true +requests_per_minute = 10 + [bird] listen = "0.0.0.0:29188" From 48f6517e1bb47e4c05e23bdfdeb271c362f86c9b Mon Sep 17 00:00:00 2001 From: hellerve Date: Tue, 13 Dec 2016 11:01:28 +0100 Subject: [PATCH 4/6] made rate limit config synchronized --- bird/bird.go | 17 ++++++++++++----- birdwatcher.go | 1 + 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/bird/bird.go b/bird/bird.go index 399146c..ae28ceb 100644 --- a/bird/bird.go +++ b/bird/bird.go @@ -9,7 +9,10 @@ import ( var ClientConf BirdConfig var StatusConf StatusConfig -var rateLimitConf RateLimitConfig +var RateLimitConf struct { + sync.Mutex + Conf RateLimitConfig +} var Cache = struct { sync.RWMutex @@ -41,21 +44,25 @@ func InstallRateLimitReset() { c := time.Tick(time.Second) for _ = range c { - rateLimitConf.Reqs = 0 + RateLimitConf.Lock() + RateLimitConf.Conf.Reqs = 0 + RateLimitConf.Unlock() } }() } func checkRateLimit() bool { - if !rateLimitConf.Enabled { + if !RateLimitConf.Conf.Enabled { return true } - if rateLimitConf.Reqs > rateLimitConf.Max { + if RateLimitConf.Conf.Reqs > RateLimitConf.Conf.Max { return false } - rateLimitConf.Reqs += 1 + RateLimitConf.Lock() + RateLimitConf.Conf.Reqs += 1 + RateLimitConf.Unlock() return true } diff --git a/birdwatcher.go b/birdwatcher.go index 5d783fb..b70bbd3 100644 --- a/birdwatcher.go +++ b/birdwatcher.go @@ -116,6 +116,7 @@ func main() { // Configuration bird.ClientConf = birdConf bird.StatusConf = conf.Status + bird.RateLimitConf.Conf = conf.Ratelimit endpoints.Conf = conf.Server // Make server From 8bd8edc90d43b91c491ca9675e593d8a3caa915c Mon Sep 17 00:00:00 2001 From: hellerve Date: Tue, 13 Dec 2016 11:05:44 +0100 Subject: [PATCH 5/6] go fmt --- endpoints/endpoint.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/endpoints/endpoint.go b/endpoints/endpoint.go index f760e7d..e364239 100644 --- a/endpoints/endpoint.go +++ b/endpoints/endpoint.go @@ -52,10 +52,10 @@ func Endpoint(wrapped func(httprouter.Params) (bird.Parsed, bool)) httprouter.Ha res := make(map[string]interface{}) ret, from_cache := wrapped(ps) - if ret == nil { - w.WriteHeader(http.StatusTooManyRequests) - return - } + if ret == nil { + w.WriteHeader(http.StatusTooManyRequests) + return + } res["api"] = GetApiInfo(from_cache) for k, v := range ret { From fcf78afddc5cc1c35efb10bf44dccf0a5ba2bdd7 Mon Sep 17 00:00:00 2001 From: hellerve Date: Tue, 13 Dec 2016 11:58:08 +0100 Subject: [PATCH 6/6] lock and unlock mutex correctly --- bird/bird.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/bird/bird.go b/bird/bird.go index ae28ceb..bdaf6f2 100644 --- a/bird/bird.go +++ b/bird/bird.go @@ -10,7 +10,7 @@ import ( var ClientConf BirdConfig var StatusConf StatusConfig var RateLimitConf struct { - sync.Mutex + sync.RWMutex Conf RateLimitConfig } @@ -52,11 +52,17 @@ func InstallRateLimitReset() { } func checkRateLimit() bool { - if !RateLimitConf.Conf.Enabled { + RateLimitConf.RLock() + check := !RateLimitConf.Conf.Enabled + RateLimitConf.RUnlock() + if check { return true } - if RateLimitConf.Conf.Reqs > RateLimitConf.Conf.Max { + RateLimitConf.RLock() + check = RateLimitConf.Conf.Reqs > RateLimitConf.Conf.Max + RateLimitConf.RUnlock() + if check { return false }