From 123cf154346c3b3dbd3849d97c7eebf9dc823b01 Mon Sep 17 00:00:00 2001 From: maxlerebourg Date: Sun, 9 Jun 2024 10:59:45 +0200 Subject: [PATCH] :sparkles: Add CrowdsecAppsecUnreachableBlock (#175) * :sparkles: Add CrowdsecAppsecUnreachableBlock * :bento: update readme * :bento: fix lint * :bento: fix lint --- README.md | 5 +++ bouncer.go | 14 +++++--- pkg/configuration/configuration.go | 56 ++++++++++++++++-------------- 3 files changed, 44 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 9ac7cab..9561643 100644 --- a/README.md +++ b/README.md @@ -333,6 +333,10 @@ Only one instance of the plugin is *possible*. - bool - default: true - Block request when Crowdsec Appsec Server have a [status 500](https://docs.crowdsec.net/docs/next/appsec/protocol#response-code). +- CrowdsecAppsecUnreachableBlock + - bool + - default: true + - Block request when Crowdsec Appsec Server is unreachable. - CrowdsecLapiScheme - string - default: `http`, expected values are: `http`, `https` @@ -486,6 +490,7 @@ http: crowdsecAppsecEnabled: false crowdsecAppsecHost: crowdsec:7422 crowdsecAppsecFailureBlock: true + crowdsecAppsecUnreachableBlock: true crowdsecLapiKey: privateKey-foo crowdsecLapiKeyFile: /etc/traefik/cs-privateKey-foo crowdsecLapiHost: crowdsec:8080 diff --git a/bouncer.go b/bouncer.go index 2d6eb9f..3afa64f 100644 --- a/bouncer.go +++ b/bouncer.go @@ -63,6 +63,7 @@ type Bouncer struct { appsecEnabled bool appsecHost string appsecFailureBlock bool + appsecUnreachableBlock bool crowdsecScheme string crowdsecHost string crowdsecKey string @@ -146,6 +147,7 @@ func New(_ context.Context, next http.Handler, config *configuration.Config, nam appsecEnabled: config.CrowdsecAppsecEnabled, appsecHost: config.CrowdsecAppsecHost, appsecFailureBlock: config.CrowdsecAppsecFailureBlock, + appsecUnreachableBlock: config.CrowdsecAppsecUnreachableBlock, crowdsecScheme: config.CrowdsecLapiScheme, crowdsecHost: config.CrowdsecLapiHost, crowdsecKey: config.CrowdsecLapiKey, @@ -546,7 +548,7 @@ func crowdsecQuery(bouncer *Bouncer, stringURL string, isPost bool) ([]byte, err req.Header.Add(bouncer.crowdsecHeader, bouncer.crowdsecKey) res, err := bouncer.httpClient.Do(req) if err != nil { - return nil, fmt.Errorf("crowdsecQuery url:%s %w", stringURL, err) + return nil, fmt.Errorf("crowdsecQuery:unreachable url:%s %w", stringURL, err) } defer func() { if err = res.Body.Close(); err != nil { @@ -602,7 +604,11 @@ func appsecQuery(bouncer *Bouncer, ip string, httpReq *http.Request) error { res, err := bouncer.httpClient.Do(req) if err != nil { - return fmt.Errorf("appsecQuery %w", err) + bouncer.log.Error("appsecQuery:unreachable") + if bouncer.appsecUnreachableBlock { + return fmt.Errorf("appsecQuery:unreachable %w", err) + } + return nil } defer func() { if err = res.Body.Close(); err != nil { @@ -610,9 +616,9 @@ func appsecQuery(bouncer *Bouncer, ip string, httpReq *http.Request) error { } }() if res.StatusCode == http.StatusInternalServerError { - bouncer.log.Debug("crowdsecQuery statusCode:500") + bouncer.log.Info("appsecQuery:failure") if bouncer.appsecFailureBlock { - return fmt.Errorf("appsecQuery statusCode:%d", res.StatusCode) + return errors.New("appsecQuery statusCode:500") } return nil } diff --git a/pkg/configuration/configuration.go b/pkg/configuration/configuration.go index a12f47e..bd136b9 100644 --- a/pkg/configuration/configuration.go +++ b/pkg/configuration/configuration.go @@ -41,6 +41,7 @@ type Config struct { CrowdsecAppsecEnabled bool `json:"crowdsecAppsecEnabled,omitempty"` CrowdsecAppsecHost string `json:"crowdsecAppsecHost,omitempty"` CrowdsecAppsecFailureBlock bool `json:"crowdsecAppsecFailureBlock,omitempty"` + CrowdsecAppsecUnreachableBlock bool `json:"crowdsecAppsecUnreachableBlock,omitempty"` CrowdsecLapiScheme string `json:"crowdsecLapiScheme,omitempty"` CrowdsecLapiHost string `json:"crowdsecLapiHost,omitempty"` CrowdsecLapiKey string `json:"crowdsecLapiKey,omitempty"` @@ -91,33 +92,34 @@ func contains(source []string, target string) bool { // New creates the default plugin configuration. func New() *Config { return &Config{ - Enabled: false, - LogLevel: "INFO", - CrowdsecMode: LiveMode, - CrowdsecAppsecEnabled: false, - CrowdsecAppsecHost: "crowdsec:7422", - CrowdsecAppsecFailureBlock: true, - CrowdsecLapiScheme: HTTP, - CrowdsecLapiHost: "crowdsec:8080", - CrowdsecLapiKey: "", - CrowdsecLapiTLSInsecureVerify: false, - UpdateIntervalSeconds: 60, - UpdateMaxFailure: 0, - DefaultDecisionSeconds: 60, - HTTPTimeoutSeconds: 10, - CaptchaProvider: "", - CaptchaSiteKey: "", - CaptchaSecretKey: "", - CaptchaGracePeriodSeconds: 1800, - CaptchaHTMLFilePath: "/captcha.html", - BanHTMLFilePath: "", - ForwardedHeadersCustomName: "X-Forwarded-For", - ForwardedHeadersTrustedIPs: []string{}, - ClientTrustedIPs: []string{}, - RedisCacheEnabled: false, - RedisCacheHost: "redis:6379", - RedisCachePassword: "", - RedisCacheDatabase: "", + Enabled: false, + LogLevel: "INFO", + CrowdsecMode: LiveMode, + CrowdsecAppsecEnabled: false, + CrowdsecAppsecHost: "crowdsec:7422", + CrowdsecAppsecFailureBlock: true, + CrowdsecAppsecUnreachableBlock: true, + CrowdsecLapiScheme: HTTP, + CrowdsecLapiHost: "crowdsec:8080", + CrowdsecLapiKey: "", + CrowdsecLapiTLSInsecureVerify: false, + UpdateIntervalSeconds: 60, + UpdateMaxFailure: 0, + DefaultDecisionSeconds: 60, + HTTPTimeoutSeconds: 10, + CaptchaProvider: "", + CaptchaSiteKey: "", + CaptchaSecretKey: "", + CaptchaGracePeriodSeconds: 1800, + CaptchaHTMLFilePath: "/captcha.html", + BanHTMLFilePath: "", + ForwardedHeadersCustomName: "X-Forwarded-For", + ForwardedHeadersTrustedIPs: []string{}, + ClientTrustedIPs: []string{}, + RedisCacheEnabled: false, + RedisCacheHost: "redis:6379", + RedisCachePassword: "", + RedisCacheDatabase: "", } }