mirror of
https://github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin.git
synced 2026-07-21 11:38:59 +02:00
✨ add remediation header when plugin made decision (#189)
* ✨ add remediation header when plugin made decision * 🍱 add documentation
This commit is contained in:
@@ -368,14 +368,18 @@ Only one instance of the plugin is *possible*.
|
|||||||
- string
|
- string
|
||||||
- default: []
|
- default: []
|
||||||
- List of client IPs to trust, they will bypass any check from the bouncer or cache (useful for LAN or VPN IP)
|
- List of client IPs to trust, they will bypass any check from the bouncer or cache (useful for LAN or VPN IP)
|
||||||
- ForwardedHeadersTrustedIPs
|
- RemediationHeadersCustomName
|
||||||
- []string
|
- string
|
||||||
- default: []
|
- default: ""
|
||||||
- List of IPs of trusted Proxies that are in front of traefik (ex: Cloudflare)
|
- Name of the header you want in response when request are cancelled (possible value of the header `ban` or `captcha`)
|
||||||
- ForwardedHeadersCustomName
|
- ForwardedHeadersCustomName
|
||||||
- string
|
- string
|
||||||
- default: "X-Forwarded-For"
|
- default: "X-Forwarded-For"
|
||||||
- Name of the header where the real IP of the client should be retrieved
|
- Name of the header where the real IP of the client should be retrieved
|
||||||
|
- ForwardedHeadersTrustedIPs
|
||||||
|
- []string
|
||||||
|
- default: []
|
||||||
|
- List of IPs of trusted Proxies that are in front of traefik (ex: Cloudflare)
|
||||||
- RedisCacheEnabled
|
- RedisCacheEnabled
|
||||||
- bool
|
- bool
|
||||||
- default: false
|
- default: false
|
||||||
@@ -508,6 +512,7 @@ http:
|
|||||||
clientTrustedIPs:
|
clientTrustedIPs:
|
||||||
- 192.168.1.0/24
|
- 192.168.1.0/24
|
||||||
forwardedHeadersCustomName: X-Custom-Header
|
forwardedHeadersCustomName: X-Custom-Header
|
||||||
|
remediationHeadersCustomName: cs-remediation
|
||||||
redisCacheEnabled: false
|
redisCacheEnabled: false
|
||||||
redisCacheHost: "redis:6379"
|
redisCacheHost: "redis:6379"
|
||||||
redisCachePassword: password
|
redisCachePassword: password
|
||||||
|
|||||||
+10
-4
@@ -74,7 +74,8 @@ type Bouncer struct {
|
|||||||
updateInterval int64
|
updateInterval int64
|
||||||
updateMaxFailure int
|
updateMaxFailure int
|
||||||
defaultDecisionTimeout int64
|
defaultDecisionTimeout int64
|
||||||
customHeader string
|
remediationCustomHeader string
|
||||||
|
forwardedCustomHeader string
|
||||||
crowdsecStreamRoute string
|
crowdsecStreamRoute string
|
||||||
crowdsecHeader string
|
crowdsecHeader string
|
||||||
banTemplateString string
|
banTemplateString string
|
||||||
@@ -156,7 +157,8 @@ func New(_ context.Context, next http.Handler, config *configuration.Config, nam
|
|||||||
crowdsecScenarios: config.CrowdsecCapiScenarios,
|
crowdsecScenarios: config.CrowdsecCapiScenarios,
|
||||||
updateInterval: config.UpdateIntervalSeconds,
|
updateInterval: config.UpdateIntervalSeconds,
|
||||||
updateMaxFailure: config.UpdateMaxFailure,
|
updateMaxFailure: config.UpdateMaxFailure,
|
||||||
customHeader: config.ForwardedHeadersCustomName,
|
remediationCustomHeader: config.RemediationHeadersCustomName,
|
||||||
|
forwardedCustomHeader: config.ForwardedHeadersCustomName,
|
||||||
defaultDecisionTimeout: config.DefaultDecisionSeconds,
|
defaultDecisionTimeout: config.DefaultDecisionSeconds,
|
||||||
banTemplateString: banTemplateString,
|
banTemplateString: banTemplateString,
|
||||||
crowdsecStreamRoute: crowdsecStreamRoute,
|
crowdsecStreamRoute: crowdsecStreamRoute,
|
||||||
@@ -202,6 +204,7 @@ func New(_ context.Context, next http.Handler, config *configuration.Config, nam
|
|||||||
config.CaptchaProvider,
|
config.CaptchaProvider,
|
||||||
config.CaptchaSiteKey,
|
config.CaptchaSiteKey,
|
||||||
config.CaptchaSecretKey,
|
config.CaptchaSecretKey,
|
||||||
|
config.RemediationHeadersCustomName,
|
||||||
config.CaptchaHTMLFilePath,
|
config.CaptchaHTMLFilePath,
|
||||||
config.CaptchaGracePeriodSeconds,
|
config.CaptchaGracePeriodSeconds,
|
||||||
)
|
)
|
||||||
@@ -236,8 +239,8 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here we check for the trusted IPs in the customHeader
|
// Here we check for the trusted IPs in the forwardedCustomHeader
|
||||||
remoteIP, err := ip.GetRemoteIP(req, bouncer.serverPoolStrategy, bouncer.customHeader)
|
remoteIP, err := ip.GetRemoteIP(req, bouncer.serverPoolStrategy, bouncer.forwardedCustomHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bouncer.log.Error(fmt.Sprintf("ServeHTTP:getRemoteIp ip:%s %s", remoteIP, err.Error()))
|
bouncer.log.Error(fmt.Sprintf("ServeHTTP:getRemoteIp ip:%s %s", remoteIP, err.Error()))
|
||||||
handleBanServeHTTP(bouncer, rw)
|
handleBanServeHTTP(bouncer, rw)
|
||||||
@@ -337,6 +340,9 @@ func handleBanServeHTTP(bouncer *Bouncer, rw http.ResponseWriter) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
|
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
if bouncer.remediationCustomHeader != "" {
|
||||||
|
rw.Header().Set(bouncer.remediationCustomHeader, "ban")
|
||||||
|
}
|
||||||
rw.WriteHeader(http.StatusForbidden)
|
rw.WriteHeader(http.StatusForbidden)
|
||||||
fmt.Fprint(rw, bouncer.banTemplateString)
|
fmt.Fprint(rw, bouncer.banTemplateString)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -75,7 +75,7 @@ func TestBouncer_ServeHTTP(t *testing.T) {
|
|||||||
crowdsecMode string
|
crowdsecMode string
|
||||||
updateInterval int64
|
updateInterval int64
|
||||||
defaultDecisionTimeout int64
|
defaultDecisionTimeout int64
|
||||||
customHeader string
|
forwardedCustomHeader string
|
||||||
clientPoolStrategy *ip.PoolStrategy
|
clientPoolStrategy *ip.PoolStrategy
|
||||||
serverPoolStrategy *ip.PoolStrategy
|
serverPoolStrategy *ip.PoolStrategy
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
@@ -105,7 +105,7 @@ func TestBouncer_ServeHTTP(t *testing.T) {
|
|||||||
crowdsecMode: tt.fields.crowdsecMode,
|
crowdsecMode: tt.fields.crowdsecMode,
|
||||||
updateInterval: tt.fields.updateInterval,
|
updateInterval: tt.fields.updateInterval,
|
||||||
defaultDecisionTimeout: tt.fields.defaultDecisionTimeout,
|
defaultDecisionTimeout: tt.fields.defaultDecisionTimeout,
|
||||||
customHeader: tt.fields.customHeader,
|
forwardedCustomHeader: tt.fields.forwardedCustomHeader,
|
||||||
clientPoolStrategy: tt.fields.clientPoolStrategy,
|
clientPoolStrategy: tt.fields.clientPoolStrategy,
|
||||||
serverPoolStrategy: tt.fields.serverPoolStrategy,
|
serverPoolStrategy: tt.fields.serverPoolStrategy,
|
||||||
httpClient: tt.fields.httpClient,
|
httpClient: tt.fields.httpClient,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ type Client struct {
|
|||||||
provider string
|
provider string
|
||||||
siteKey string
|
siteKey string
|
||||||
secretKey string
|
secretKey string
|
||||||
|
remediationCustomHeader string
|
||||||
gracePeriodSeconds int64
|
gracePeriodSeconds int64
|
||||||
captchaTemplate *template.Template
|
captchaTemplate *template.Template
|
||||||
cacheClient *cache.Client
|
cacheClient *cache.Client
|
||||||
@@ -55,7 +56,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// New Initialize captcha client.
|
// New Initialize captcha client.
|
||||||
func (c *Client) New(log *logger.Log, cacheClient *cache.Client, httpClient *http.Client, provider, siteKey, secretKey, captchaTemplatePath string, gracePeriodSeconds int64) error {
|
func (c *Client) New(log *logger.Log, cacheClient *cache.Client, httpClient *http.Client, provider, siteKey, secretKey, remediationCustomHeader, captchaTemplatePath string, gracePeriodSeconds int64) error {
|
||||||
c.Valid = provider != ""
|
c.Valid = provider != ""
|
||||||
if !c.Valid {
|
if !c.Valid {
|
||||||
return nil
|
return nil
|
||||||
@@ -63,6 +64,7 @@ func (c *Client) New(log *logger.Log, cacheClient *cache.Client, httpClient *htt
|
|||||||
c.siteKey = siteKey
|
c.siteKey = siteKey
|
||||||
c.secretKey = secretKey
|
c.secretKey = secretKey
|
||||||
c.provider = provider
|
c.provider = provider
|
||||||
|
c.remediationCustomHeader = remediationCustomHeader
|
||||||
html, _ := configuration.GetHTMLTemplate(captchaTemplatePath)
|
html, _ := configuration.GetHTMLTemplate(captchaTemplatePath)
|
||||||
c.captchaTemplate = html
|
c.captchaTemplate = html
|
||||||
c.gracePeriodSeconds = gracePeriodSeconds
|
c.gracePeriodSeconds = gracePeriodSeconds
|
||||||
@@ -87,6 +89,9 @@ func (c *Client) ServeHTTP(rw http.ResponseWriter, r *http.Request, remoteIP str
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
|
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
if c.remediationCustomHeader != "" {
|
||||||
|
rw.Header().Set(c.remediationCustomHeader, "captcha")
|
||||||
|
}
|
||||||
rw.WriteHeader(http.StatusOK)
|
rw.WriteHeader(http.StatusOK)
|
||||||
err = c.captchaTemplate.Execute(rw, map[string]string{
|
err = c.captchaTemplate.Execute(rw, map[string]string{
|
||||||
"SiteKey": c.siteKey,
|
"SiteKey": c.siteKey,
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ type Config struct {
|
|||||||
UpdateMaxFailure int `json:"updateMaxFailure,omitempty"`
|
UpdateMaxFailure int `json:"updateMaxFailure,omitempty"`
|
||||||
DefaultDecisionSeconds int64 `json:"defaultDecisionSeconds,omitempty"`
|
DefaultDecisionSeconds int64 `json:"defaultDecisionSeconds,omitempty"`
|
||||||
HTTPTimeoutSeconds int64 `json:"httpTimeoutSeconds,omitempty"`
|
HTTPTimeoutSeconds int64 `json:"httpTimeoutSeconds,omitempty"`
|
||||||
|
RemediationHeadersCustomName string `json:"remediationHeadersCustomName,omitempty"`
|
||||||
ForwardedHeadersCustomName string `json:"forwardedHeadersCustomName,omitempty"`
|
ForwardedHeadersCustomName string `json:"forwardedHeadersCustomName,omitempty"`
|
||||||
ForwardedHeadersTrustedIPs []string `json:"forwardedHeadersTrustedIps,omitempty"`
|
ForwardedHeadersTrustedIPs []string `json:"forwardedHeadersTrustedIps,omitempty"`
|
||||||
ClientTrustedIPs []string `json:"clientTrustedIps,omitempty"`
|
ClientTrustedIPs []string `json:"clientTrustedIps,omitempty"`
|
||||||
@@ -113,6 +114,7 @@ func New() *Config {
|
|||||||
CaptchaGracePeriodSeconds: 1800,
|
CaptchaGracePeriodSeconds: 1800,
|
||||||
CaptchaHTMLFilePath: "/captcha.html",
|
CaptchaHTMLFilePath: "/captcha.html",
|
||||||
BanHTMLFilePath: "",
|
BanHTMLFilePath: "",
|
||||||
|
RemediationHeadersCustomName: "",
|
||||||
ForwardedHeadersCustomName: "X-Forwarded-For",
|
ForwardedHeadersCustomName: "X-Forwarded-For",
|
||||||
ForwardedHeadersTrustedIPs: []string{},
|
ForwardedHeadersTrustedIPs: []string{},
|
||||||
ClientTrustedIPs: []string{},
|
ClientTrustedIPs: []string{},
|
||||||
|
|||||||
Reference in New Issue
Block a user