From 15bca90b181783123fd10d7701919d6d713848d3 Mon Sep 17 00:00:00 2001 From: Max Lerebourg Date: Sat, 19 Nov 2022 22:17:06 +0100 Subject: [PATCH 1/8] :bento: fix loging --- bouncer.go | 33 ++++++++++++++++----------------- pkg/ip/ip.go | 10 +++++----- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/bouncer.go b/bouncer.go index 2a760cb..e9e8b67 100644 --- a/bouncer.go +++ b/bouncer.go @@ -175,7 +175,7 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if bouncer.crowdsecMode != noneMode { isBanned, err := cache.GetDecision(remoteIP) if err != nil { - logger.Error(err.Error()) + logger.Debug(err.Error()) if err.Error() == simpleredis.RedisUnreachable { healthy = false } @@ -259,7 +259,7 @@ func handleNoStreamCache(bouncer *Bouncer, rw http.ResponseWriter, req *http.Req } body, err := crowdsecQuery(bouncer, routeURL.String()) if err != nil { - logger.Info(err.Error()) + logger.Error(err.Error()) rw.WriteHeader(http.StatusForbidden) return } @@ -275,7 +275,7 @@ func handleNoStreamCache(bouncer *Bouncer, rw http.ResponseWriter, req *http.Req var decisions []Decision err = json.Unmarshal(body, &decisions) if err != nil { - logger.Info(fmt.Sprintf("failed to parse body: %s", err)) + logger.Error(fmt.Sprintf("handleNoStreamCache:parseBody: %s", err)) rw.WriteHeader(http.StatusForbidden) return } @@ -289,7 +289,7 @@ func handleNoStreamCache(bouncer *Bouncer, rw http.ResponseWriter, req *http.Req rw.WriteHeader(http.StatusForbidden) duration, err := time.ParseDuration(decisions[0].Duration) if err != nil { - logger.Info(fmt.Sprintf("failed to parse duration: %s", err)) + logger.Error(fmt.Sprintf("handleNoStreamCache:parseDuration %s", err)) return } if bouncer.crowdsecMode == liveMode { @@ -302,9 +302,9 @@ func handleStreamCache(bouncer *Bouncer) { // Instead of blocking the goroutine interval for all the secondary node, // if the master service is shut down, other goroutine can take the lead // because updated routine information is in the cache - logger.Debug("handleStreamCache") _, err := cache.GetDecision(cacheTimeoutKey) if err == nil { + logger.Debug("handleStreamCache:alreadyUpdated") return } cache.SetDecision(cacheTimeoutKey, false, bouncer.updateInterval-1) @@ -316,14 +316,14 @@ func handleStreamCache(bouncer *Bouncer) { } body, err := crowdsecQuery(bouncer, streamRouteURL.String()) if err != nil { - logger.Info(err.Error()) + logger.Error(err.Error()) crowdsecStreamHealthy = false return } var stream Stream err = json.Unmarshal(body, &stream) if err != nil { - logger.Info(fmt.Sprintf("error while parsing body: %s", err)) + logger.Error(fmt.Sprintf("handleStreamCache:parsingBody %s", err)) crowdsecStreamHealthy = false return } @@ -345,21 +345,20 @@ func crowdsecQuery(bouncer *Bouncer, stringURL string) ([]byte, error) { req.Header.Add(crowdsecLapiHeader, bouncer.crowdsecKey) res, err := bouncer.client.Do(req) if err != nil { - return nil, fmt.Errorf("error while fetching %v: %w", stringURL, err) + return nil, fmt.Errorf("crowdsecQuery url:%s %s", stringURL, err.Error()) } if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf("error while fetching %v, status code: %d", stringURL, res.StatusCode) + return nil, fmt.Errorf("crowdsecQuery url:%s, statusCode:%d", stringURL, res.StatusCode) } - defer func(body io.ReadCloser) { - err = body.Close() - if err != nil { - logger.Error(fmt.Sprintf("failed to close body reader: %s", err.Error())) + defer func() { + if err = res.Body.Close(); err != nil { + logger.Error(fmt.Sprintf("crowdsecQuery:closeBody %s", err.Error())) } - }(res.Body) + }() body, err := io.ReadAll(res.Body) if err != nil { - return nil, fmt.Errorf("error while reading body: %w", err) + return nil, fmt.Errorf("crowdsecQuery:readBody %s", err.Error()) } return body, nil } @@ -402,7 +401,7 @@ func validateParams(config *Config) error { if len(config.ForwardedHeadersTrustedIPs) > 0 { _, err = ip.NewChecker(config.ForwardedHeadersTrustedIPs) if err != nil { - return fmt.Errorf("ForwardedHeadersTrustedIPs must be a list of IP/CIDR :%w", err) + return fmt.Errorf("ForwardedHeadersTrustedIPs must be a list of IP/CIDR :%s", err.Error()) } } else { logger.Debug("No IP provided for ForwardedHeadersTrustedIPs") @@ -410,7 +409,7 @@ func validateParams(config *Config) error { if len(config.ClientTrustedIPs) > 0 { _, err = ip.NewChecker(config.ClientTrustedIPs) if err != nil { - return fmt.Errorf("TrustedIPs must be a list of IP/CIDR :%w", err) + return fmt.Errorf("TrustedIPs must be a list of IP/CIDR :%s", err.Error()) } } else { logger.Debug("No IP provided for TrustedIPs") diff --git a/pkg/ip/ip.go b/pkg/ip/ip.go index f2b167e..b46e9e8 100644 --- a/pkg/ip/ip.go +++ b/pkg/ip/ip.go @@ -33,7 +33,7 @@ func NewChecker(trustedIPs []string) (*Checker, error) { _, ipAddr, err := net.ParseCIDR(ipMask) if err != nil { - return nil, fmt.Errorf("parsing CIDR trusted IPs %s: %w", ipAddr, err) + return nil, fmt.Errorf("parsing CIDR trusted IPs %s: %s", ipAddr, err.Error()) } checker.authorizedIPsNet = append(checker.authorizedIPsNet, ipAddr) logger.Debug(fmt.Sprintf("IP network %v is trusted", ipAddr)) @@ -45,12 +45,12 @@ func NewChecker(trustedIPs []string) (*Checker, error) { // Contains checks if provided address is in the trusted IPs. func (ip *Checker) Contains(addr string) (bool, error) { if len(addr) == 0 { - return false, errors.New("empty IP address") + return false, errors.New("Contains:noAddress") } ipAddr, err := parseIP(addr) if err != nil { - return false, fmt.Errorf("unable to parse address: %s: %w", addr, err) + return false, fmt.Errorf("Contains:parseAddress addr:%s %s", addr, err.Error()) } return ip.ContainsIP(ipAddr), nil @@ -76,7 +76,7 @@ func (ip *Checker) ContainsIP(addr net.IP) bool { func parseIP(addr string) (net.IP, error) { userIP := net.ParseIP(addr) if userIP == nil { - return nil, fmt.Errorf("can't parse IP from address %s", addr) + return nil, fmt.Errorf("parseIP:parseAddress %s", addr) } return userIP, nil @@ -123,7 +123,7 @@ func GetRemoteIP(req *http.Request, strategy *PoolStrategy, customHeader string) } remoteIP, _, err := net.SplitHostPort(req.RemoteAddr) if err != nil { - return "", fmt.Errorf("failed to extract ip from remote address: %w", err) + return "", fmt.Errorf("GetRemoteIP:extractIP: %s", err.Error()) } return remoteIP, nil } From 72be1a68f5bf5a64981cb245add033fdd9decbf0 Mon Sep 17 00:00:00 2001 From: Max Lerebourg Date: Sat, 19 Nov 2022 22:18:47 +0100 Subject: [PATCH 2/8] :bento: fix loging --- pkg/ip/ip.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/ip/ip.go b/pkg/ip/ip.go index b46e9e8..fb1abd2 100644 --- a/pkg/ip/ip.go +++ b/pkg/ip/ip.go @@ -3,7 +3,6 @@ package ip import ( - "errors" "fmt" "net" "net/http" @@ -45,7 +44,7 @@ func NewChecker(trustedIPs []string) (*Checker, error) { // Contains checks if provided address is in the trusted IPs. func (ip *Checker) Contains(addr string) (bool, error) { if len(addr) == 0 { - return false, errors.New("Contains:noAddress") + return false, fmt.Errorf("Contains:noAddress") } ipAddr, err := parseIP(addr) From 80d49e39699a5c27c210d61f5a5e59d64af11c8b Mon Sep 17 00:00:00 2001 From: Max Lerebourg Date: Sat, 19 Nov 2022 22:23:12 +0100 Subject: [PATCH 3/8] :bento: fix loging --- bouncer.go | 18 +++++++++--------- pkg/ip/ip.go | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bouncer.go b/bouncer.go index e9e8b67..187c2fa 100644 --- a/bouncer.go +++ b/bouncer.go @@ -154,7 +154,7 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // Here we check for the trusted IPs in the customHeader remoteIP, err := ip.GetRemoteIP(req, bouncer.serverPoolStrategy, bouncer.customHeader) if err != nil { - logger.Error(fmt.Sprintf("ServeHTTP ip:%s %s", remoteIP, err.Error())) + logger.Error(fmt.Sprintf("ServeHTTP ip:%s %w", remoteIP, err)) rw.WriteHeader(http.StatusForbidden) return } @@ -275,7 +275,7 @@ func handleNoStreamCache(bouncer *Bouncer, rw http.ResponseWriter, req *http.Req var decisions []Decision err = json.Unmarshal(body, &decisions) if err != nil { - logger.Error(fmt.Sprintf("handleNoStreamCache:parseBody: %s", err)) + logger.Error(fmt.Sprintf("handleNoStreamCache:parseBody: %w", err)) rw.WriteHeader(http.StatusForbidden) return } @@ -289,7 +289,7 @@ func handleNoStreamCache(bouncer *Bouncer, rw http.ResponseWriter, req *http.Req rw.WriteHeader(http.StatusForbidden) duration, err := time.ParseDuration(decisions[0].Duration) if err != nil { - logger.Error(fmt.Sprintf("handleNoStreamCache:parseDuration %s", err)) + logger.Error(fmt.Sprintf("handleNoStreamCache:parseDuration %w", err)) return } if bouncer.crowdsecMode == liveMode { @@ -323,7 +323,7 @@ func handleStreamCache(bouncer *Bouncer) { var stream Stream err = json.Unmarshal(body, &stream) if err != nil { - logger.Error(fmt.Sprintf("handleStreamCache:parsingBody %s", err)) + logger.Error(fmt.Sprintf("handleStreamCache:parsingBody %w", err)) crowdsecStreamHealthy = false return } @@ -345,20 +345,20 @@ func crowdsecQuery(bouncer *Bouncer, stringURL string) ([]byte, error) { req.Header.Add(crowdsecLapiHeader, bouncer.crowdsecKey) res, err := bouncer.client.Do(req) if err != nil { - return nil, fmt.Errorf("crowdsecQuery url:%s %s", stringURL, err.Error()) + return nil, fmt.Errorf("crowdsecQuery url:%s %w", stringURL, err) } if res.StatusCode != http.StatusOK { return nil, fmt.Errorf("crowdsecQuery url:%s, statusCode:%d", stringURL, res.StatusCode) } defer func() { if err = res.Body.Close(); err != nil { - logger.Error(fmt.Sprintf("crowdsecQuery:closeBody %s", err.Error())) + logger.Error(fmt.Sprintf("crowdsecQuery:closeBody %w", err)) } }() body, err := io.ReadAll(res.Body) if err != nil { - return nil, fmt.Errorf("crowdsecQuery:readBody %s", err.Error()) + return nil, fmt.Errorf("crowdsecQuery:readBody %w", err) } return body, nil } @@ -401,7 +401,7 @@ func validateParams(config *Config) error { if len(config.ForwardedHeadersTrustedIPs) > 0 { _, err = ip.NewChecker(config.ForwardedHeadersTrustedIPs) if err != nil { - return fmt.Errorf("ForwardedHeadersTrustedIPs must be a list of IP/CIDR :%s", err.Error()) + return fmt.Errorf("ForwardedHeadersTrustedIPs must be a list of IP/CIDR :%w", err) } } else { logger.Debug("No IP provided for ForwardedHeadersTrustedIPs") @@ -409,7 +409,7 @@ func validateParams(config *Config) error { if len(config.ClientTrustedIPs) > 0 { _, err = ip.NewChecker(config.ClientTrustedIPs) if err != nil { - return fmt.Errorf("TrustedIPs must be a list of IP/CIDR :%s", err.Error()) + return fmt.Errorf("TrustedIPs must be a list of IP/CIDR :%w", err) } } else { logger.Debug("No IP provided for TrustedIPs") diff --git a/pkg/ip/ip.go b/pkg/ip/ip.go index fb1abd2..303b7a4 100644 --- a/pkg/ip/ip.go +++ b/pkg/ip/ip.go @@ -32,7 +32,7 @@ func NewChecker(trustedIPs []string) (*Checker, error) { _, ipAddr, err := net.ParseCIDR(ipMask) if err != nil { - return nil, fmt.Errorf("parsing CIDR trusted IPs %s: %s", ipAddr, err.Error()) + return nil, fmt.Errorf("parsing CIDR trusted IPs %s: %w", ipAddr, err) } checker.authorizedIPsNet = append(checker.authorizedIPsNet, ipAddr) logger.Debug(fmt.Sprintf("IP network %v is trusted", ipAddr)) @@ -49,7 +49,7 @@ func (ip *Checker) Contains(addr string) (bool, error) { ipAddr, err := parseIP(addr) if err != nil { - return false, fmt.Errorf("Contains:parseAddress addr:%s %s", addr, err.Error()) + return false, fmt.Errorf("Contains:parseAddress addr:%s %w", addr, err) } return ip.ContainsIP(ipAddr), nil @@ -122,7 +122,7 @@ func GetRemoteIP(req *http.Request, strategy *PoolStrategy, customHeader string) } remoteIP, _, err := net.SplitHostPort(req.RemoteAddr) if err != nil { - return "", fmt.Errorf("GetRemoteIP:extractIP: %s", err.Error()) + return "", fmt.Errorf("GetRemoteIP:extractIP: %w", err) } return remoteIP, nil } From d1d64689af754476534744b9b93620904c735408 Mon Sep 17 00:00:00 2001 From: Max Lerebourg Date: Sun, 20 Nov 2022 12:04:05 +0100 Subject: [PATCH 4/8] :bento: fix logic + logs --- bouncer.go | 82 +++++++++---------- exemples/redis-cache/docker-compose.redis.yml | 6 +- 2 files changed, 42 insertions(+), 46 deletions(-) diff --git a/bouncer.go b/bouncer.go index 187c2fa..511e1f2 100644 --- a/bouncer.go +++ b/bouncer.go @@ -31,8 +31,8 @@ const ( //nolint:gochecknoglobals var ( - crowdsecStreamHealthy = false - ticker chan bool + isCrowdsecStreamHealthy = false + ticker chan bool ) // Config the plugin configuration. @@ -154,51 +154,45 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // Here we check for the trusted IPs in the customHeader remoteIP, err := ip.GetRemoteIP(req, bouncer.serverPoolStrategy, bouncer.customHeader) if err != nil { - logger.Error(fmt.Sprintf("ServeHTTP ip:%s %w", remoteIP, err)) + logger.Error(fmt.Sprintf("ServeHTTP ip:%s %s", remoteIP, err.Error())) rw.WriteHeader(http.StatusForbidden) return } - trusted, err := bouncer.clientPoolStrategy.Checker.Contains(remoteIP) + isTrusted, err := bouncer.clientPoolStrategy.Checker.Contains(remoteIP) if err != nil { - logger.Info(err.Error()) + logger.Error(err.Error()) + rw.WriteHeader(http.StatusForbidden) return } // if our IP is in the trusted list we bypass the next checks - logger.Debug(fmt.Sprintf("ServeHTTP ip:%s isTrusted:%v", remoteIP, trusted)) - if trusted { + logger.Debug(fmt.Sprintf("ServeHTTP ip:%s isTrusted:%v", remoteIP, isTrusted)) + if isTrusted { bouncer.next.ServeHTTP(rw, req) return } // TODO This should be simplified - healthy := crowdsecStreamHealthy if bouncer.crowdsecMode != noneMode { isBanned, err := cache.GetDecision(remoteIP) if err != nil { logger.Debug(err.Error()) if err.Error() == simpleredis.RedisUnreachable { - healthy = false + rw.WriteHeader(http.StatusForbidden) + return } } else { logger.Debug(fmt.Sprintf("ServeHTTP ip:%s cache:hit isBanned:%v", remoteIP, isBanned)) - if isBanned { - rw.WriteHeader(http.StatusForbidden) - } else { - bouncer.next.ServeHTTP(rw, req) - } + response(isBanned, bouncer, rw, req) return } } // Right here if we cannot join the stream we forbid the request to go on. if bouncer.crowdsecMode == streamMode { - if healthy { - bouncer.next.ServeHTTP(rw, req) - } else { - rw.WriteHeader(http.StatusForbidden) - } + response(isCrowdsecStreamHealthy, bouncer, rw, req) } else { - handleNoStreamCache(bouncer, rw, req, remoteIP) + err = handleNoStreamCache(bouncer, remoteIP) + response(err != nil, bouncer, rw, req) } } @@ -223,6 +217,14 @@ type Stream struct { New []Decision `json:"new"` } +func response(isValid bool, bouncer *Bouncer, rw http.ResponseWriter, req *http.Request) { + if isValid { + rw.WriteHeader(http.StatusForbidden) + } else { + bouncer.next.ServeHTTP(rw, req) + } +} + func contains(source []string, target string) bool { for _, item := range source { if item == target { @@ -250,7 +252,8 @@ func startTicker(config *Config, work func()) chan bool { } // We are now in none or live mode. -func handleNoStreamCache(bouncer *Bouncer, rw http.ResponseWriter, req *http.Request, remoteIP string) { +func handleNoStreamCache(bouncer *Bouncer, remoteIP string) error { + isLiveMode := bouncer.crowdsecMode == liveMode routeURL := url.URL{ Scheme: bouncer.crowdsecScheme, Host: bouncer.crowdsecHost, @@ -259,42 +262,35 @@ func handleNoStreamCache(bouncer *Bouncer, rw http.ResponseWriter, req *http.Req } body, err := crowdsecQuery(bouncer, routeURL.String()) if err != nil { - logger.Error(err.Error()) - rw.WriteHeader(http.StatusForbidden) - return + return err } if bytes.Equal(body, []byte("null")) { - if bouncer.crowdsecMode == liveMode { + if isLiveMode { cache.SetDecision(remoteIP, false, bouncer.defaultDecisionTimeout) } - bouncer.next.ServeHTTP(rw, req) - return + return nil } var decisions []Decision err = json.Unmarshal(body, &decisions) if err != nil { - logger.Error(fmt.Sprintf("handleNoStreamCache:parseBody: %w", err)) - rw.WriteHeader(http.StatusForbidden) - return + return fmt.Errorf("handleNoStreamCache:parseBody: %w", err) } if len(decisions) == 0 { - if bouncer.crowdsecMode == liveMode { + if isLiveMode { cache.SetDecision(remoteIP, false, bouncer.defaultDecisionTimeout) } - bouncer.next.ServeHTTP(rw, req) - return + return nil } - rw.WriteHeader(http.StatusForbidden) duration, err := time.ParseDuration(decisions[0].Duration) if err != nil { - logger.Error(fmt.Sprintf("handleNoStreamCache:parseDuration %w", err)) - return + return fmt.Errorf("handleNoStreamCache:parseDuration %w", err) } - if bouncer.crowdsecMode == liveMode { + if isLiveMode { cache.SetDecision(remoteIP, true, int64(duration.Seconds())) } + return fmt.Errorf("handleNoStreamCache:banned") } func handleStreamCache(bouncer *Bouncer) { @@ -312,19 +308,19 @@ func handleStreamCache(bouncer *Bouncer) { Scheme: bouncer.crowdsecScheme, Host: bouncer.crowdsecHost, Path: crowdsecLapiStreamRoute, - RawQuery: fmt.Sprintf("startup=%t", !crowdsecStreamHealthy), + RawQuery: fmt.Sprintf("startup=%t", !isCrowdsecStreamHealthy), } body, err := crowdsecQuery(bouncer, streamRouteURL.String()) if err != nil { logger.Error(err.Error()) - crowdsecStreamHealthy = false + isCrowdsecStreamHealthy = false return } var stream Stream err = json.Unmarshal(body, &stream) if err != nil { - logger.Error(fmt.Sprintf("handleStreamCache:parsingBody %w", err)) - crowdsecStreamHealthy = false + logger.Error(fmt.Sprintf("handleStreamCache:parsingBody %s", err.Error())) + isCrowdsecStreamHealthy = false return } for _, decision := range stream.New { @@ -336,7 +332,7 @@ func handleStreamCache(bouncer *Bouncer) { for _, decision := range stream.Deleted { cache.DeleteDecision(decision.Value) } - crowdsecStreamHealthy = true + isCrowdsecStreamHealthy = true } func crowdsecQuery(bouncer *Bouncer, stringURL string) ([]byte, error) { @@ -352,7 +348,7 @@ func crowdsecQuery(bouncer *Bouncer, stringURL string) ([]byte, error) { } defer func() { if err = res.Body.Close(); err != nil { - logger.Error(fmt.Sprintf("crowdsecQuery:closeBody %w", err)) + logger.Error(fmt.Sprintf("crowdsecQuery:closeBody %s", err.Error())) } }() body, err := io.ReadAll(res.Body) diff --git a/exemples/redis-cache/docker-compose.redis.yml b/exemples/redis-cache/docker-compose.redis.yml index 0252418..a035ea4 100644 --- a/exemples/redis-cache/docker-compose.redis.yml +++ b/exemples/redis-cache/docker-compose.redis.yml @@ -14,9 +14,9 @@ services: - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" - - "--experimental.plugins.bouncer.version=v1.1.3" - # - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" + # - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" + # - "--experimental.plugins.bouncer.version=v1.1.3" + - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - logs-redis:/var/log/traefik From 953e9f6bf447e2ead9fe66819c8d6db5d046cd6a Mon Sep 17 00:00:00 2001 From: Max Lerebourg Date: Sun, 20 Nov 2022 12:12:20 +0100 Subject: [PATCH 5/8] :bento: fix lint --- bouncer.go | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/bouncer.go b/bouncer.go index 511e1f2..6655d81 100644 --- a/bouncer.go +++ b/bouncer.go @@ -126,7 +126,7 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h MaxIdleConns: 10, IdleConnTimeout: 30 * time.Second, }, - Timeout: 5 * time.Second, + Timeout: 2 * time.Second, }, } if config.RedisCacheEnabled { @@ -182,17 +182,30 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } } else { logger.Debug(fmt.Sprintf("ServeHTTP ip:%s cache:hit isBanned:%v", remoteIP, isBanned)) - response(isBanned, bouncer, rw, req) + if isBanned { + rw.WriteHeader(http.StatusForbidden) + } else { + bouncer.next.ServeHTTP(rw, req) + } return } } // Right here if we cannot join the stream we forbid the request to go on. if bouncer.crowdsecMode == streamMode { - response(isCrowdsecStreamHealthy, bouncer, rw, req) + if isCrowdsecStreamHealthy { + rw.WriteHeader(http.StatusForbidden) + } else { + bouncer.next.ServeHTTP(rw, req) + } } else { err = handleNoStreamCache(bouncer, remoteIP) - response(err != nil, bouncer, rw, req) + if err != nil { + logger.Debug(err.Error()) + rw.WriteHeader(http.StatusForbidden) + } else { + bouncer.next.ServeHTTP(rw, req) + } } } @@ -217,14 +230,6 @@ type Stream struct { New []Decision `json:"new"` } -func response(isValid bool, bouncer *Bouncer, rw http.ResponseWriter, req *http.Request) { - if isValid { - rw.WriteHeader(http.StatusForbidden) - } else { - bouncer.next.ServeHTTP(rw, req) - } -} - func contains(source []string, target string) bool { for _, item := range source { if item == target { From 1dc50a8a8c8760b9663fd798ad084725bb1bb625 Mon Sep 17 00:00:00 2001 From: Max Lerebourg Date: Sun, 20 Nov 2022 12:29:00 +0100 Subject: [PATCH 6/8] :bento: add more error context --- bouncer.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bouncer.go b/bouncer.go index 6655d81..6c179d3 100644 --- a/bouncer.go +++ b/bouncer.go @@ -154,13 +154,13 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // Here we check for the trusted IPs in the customHeader remoteIP, err := ip.GetRemoteIP(req, bouncer.serverPoolStrategy, bouncer.customHeader) if err != nil { - logger.Error(fmt.Sprintf("ServeHTTP ip:%s %s", remoteIP, err.Error())) + logger.Error(fmt.Sprintf("ServeHTTP:getRemoteIp ip:%s %s", remoteIP, err.Error())) rw.WriteHeader(http.StatusForbidden) return } isTrusted, err := bouncer.clientPoolStrategy.Checker.Contains(remoteIP) if err != nil { - logger.Error(err.Error()) + logger.Error(fmt.Sprintf("ServeHTTP:checkerContains ip:%s %s", remoteIP, err.Error())) rw.WriteHeader(http.StatusForbidden) return } @@ -175,7 +175,7 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if bouncer.crowdsecMode != noneMode { isBanned, err := cache.GetDecision(remoteIP) if err != nil { - logger.Debug(err.Error()) + logger.Debug(fmt.Sprintf("ServeHTTP:getDecision ip:%s %s", remoteIP, err.Error())) if err.Error() == simpleredis.RedisUnreachable { rw.WriteHeader(http.StatusForbidden) return @@ -194,6 +194,7 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // Right here if we cannot join the stream we forbid the request to go on. if bouncer.crowdsecMode == streamMode { if isCrowdsecStreamHealthy { + logger.Error(fmt.Sprintf("ServeHTTP:isCrowdsecStreamHealthy ip:%s", remoteIP)) rw.WriteHeader(http.StatusForbidden) } else { bouncer.next.ServeHTTP(rw, req) @@ -280,7 +281,7 @@ func handleNoStreamCache(bouncer *Bouncer, remoteIP string) error { var decisions []Decision err = json.Unmarshal(body, &decisions) if err != nil { - return fmt.Errorf("handleNoStreamCache:parseBody: %w", err) + return fmt.Errorf("handleNoStreamCache:parseBody %w", err) } if len(decisions) == 0 { if isLiveMode { From b37f866ca9e6f852aab3bfe8129f929116da35e3 Mon Sep 17 00:00:00 2001 From: Max Lerebourg Date: Sun, 20 Nov 2022 12:33:39 +0100 Subject: [PATCH 7/8] :bento: fix lint :vomit: --- bouncer.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bouncer.go b/bouncer.go index 6c179d3..5b0f01a 100644 --- a/bouncer.go +++ b/bouncer.go @@ -173,10 +173,10 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // TODO This should be simplified if bouncer.crowdsecMode != noneMode { - isBanned, err := cache.GetDecision(remoteIP) - if err != nil { - logger.Debug(fmt.Sprintf("ServeHTTP:getDecision ip:%s %s", remoteIP, err.Error())) - if err.Error() == simpleredis.RedisUnreachable { + isBanned, erro := cache.GetDecision(remoteIP) + if erro != nil { + logger.Debug(fmt.Sprintf("ServeHTTP:getDecision ip:%s %s", remoteIP, erro.Error())) + if erro.Error() == simpleredis.RedisUnreachable { rw.WriteHeader(http.StatusForbidden) return } From f99dbfc2031913ef61c9a5a7ad63f09749ffab1f Mon Sep 17 00:00:00 2001 From: Max Lerebourg Date: Sun, 20 Nov 2022 12:46:05 +0100 Subject: [PATCH 8/8] :bento: fix comment --- exemples/redis-cache/docker-compose.redis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exemples/redis-cache/docker-compose.redis.yml b/exemples/redis-cache/docker-compose.redis.yml index a035ea4..0252418 100644 --- a/exemples/redis-cache/docker-compose.redis.yml +++ b/exemples/redis-cache/docker-compose.redis.yml @@ -14,9 +14,9 @@ services: - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - # - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" - # - "--experimental.plugins.bouncer.version=v1.1.3" - - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" + - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" + - "--experimental.plugins.bouncer.version=v1.1.3" + # - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - logs-redis:/var/log/traefik