Compare commits

...

11 Commits

Author SHA1 Message Date
maxlerebourg
80e1945633 🍱 lint html 2025-11-07 00:29:47 +01:00
maxlerebourg
06a88c89ac 🍱 fix lint 2025-11-07 00:27:31 +01:00
maxlerebourg
18011fabe6 🍱 fix lint 2025-11-07 00:26:25 +01:00
maxlerebourg
5b147c7b27 🍱 fix lint 2025-11-07 00:24:44 +01:00
maxlerebourg
959b792111 🍱 fix lint 2025-11-07 00:21:27 +01:00
maxlerebourg
ac317f45aa 🍱 fix lint 2025-11-07 00:20:27 +01:00
maxlerebourg
a35c74a31d 🍱 add doc and fix lint 2025-11-07 00:19:25 +01:00
maxlerebourg
60c5ae742d 🍱 fix lint 2025-11-07 00:12:24 +01:00
maxlerebourg
1ea6af5ee3 🍱 fix lint 2025-11-07 00:10:40 +01:00
maxlerebourg
0ad6f13e34 🍱 fix test 2025-11-07 00:04:53 +01:00
maxlerebourg
6afff87214 🍱 fix lint 2025-11-06 22:56:09 +01:00
4 changed files with 30 additions and 22 deletions

View File

@@ -281,7 +281,6 @@
</head>
<body class="h-screen w-screen p-4">
<script>var reason = "{{ .RemediationReason }}"</script>
<div class="h-full w-full flex flex-col justify-center items-center">
<div class="border-2 border-black rounded-xl p-4 text-center w-full sm:w-2/3 lg:w-1/2">
<div class="flex flex-col items-center space-y-4">
@@ -327,5 +326,4 @@
</div>
</div>
</body>
</html>
</html>

View File

@@ -9,7 +9,7 @@ import (
"encoding/json"
"errors"
"fmt"
httptemplate "html/template"
htmltemplate "html/template"
"io"
"net/http"
"net/url"
@@ -107,7 +107,7 @@ type Bouncer struct {
crowdsecStreamRoute string
crowdsecHeader string
redisUnreachableBlock bool
banTemplate *httptemplate.Template
banTemplate *htmltemplate.Template
clientPoolStrategy *ip.PoolStrategy
serverPoolStrategy *ip.PoolStrategy
httpClient *http.Client
@@ -160,10 +160,10 @@ func New(_ context.Context, next http.Handler, config *configuration.Config, nam
config.CrowdsecLapiKey = apiKey
}
var banTemplate *httptemplate.Template
if config.BanHTMLFilePath != "" {
banTemplate, _ = configuration.GetHTMLTemplate(config.BanHTMLFilePath)
}
var banTemplate *htmltemplate.Template
if config.BanHTMLFilePath != "" {
banTemplate, _ = configuration.GetHTMLTemplate(config.BanHTMLFilePath)
}
bouncer := &Bouncer{
next: next,
@@ -305,12 +305,12 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
bouncer.next.ServeHTTP(rw, req)
return
}
if bouncer.crowdsecMode == configuration.AppsecMode {
handleNextServeHTTP(bouncer, remoteIP, rw, req)
return
}
// TODO This should be simplified
if bouncer.crowdsecMode != configuration.NoneMode {
value, cacheErr := bouncer.cacheClient.Get(remoteIP)
@@ -331,13 +331,13 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
bouncer.log.Debug(fmt.Sprintf("ServeHTTP ip:%s cache:hit isBanned:%v", remoteIP, value))
if value == cache.NoBannedValue {
handleNextServeHTTP(bouncer, remoteIP, rw, req)
} else {
} else {
handleRemediationServeHTTP(bouncer, remoteIP, value, rw, req)
}
return
}
}
// Right here if we cannot join the stream we forbid the request to go on.
if bouncer.crowdsecMode == configuration.StreamMode || bouncer.crowdsecMode == configuration.AloneMode {
if isCrowdsecStreamHealthy {
@@ -388,7 +388,7 @@ type Login struct {
// To append Headers we need to call rw.WriteHeader after set any header.
func handleBanServeHTTP(bouncer *Bouncer, rw http.ResponseWriter, method, reason string) {
atomic.AddInt64(&blockedRequests, 1)
if bouncer.remediationCustomHeader != "" {
rw.Header().Set(bouncer.remediationCustomHeader, "ban")
}

View File

@@ -2,7 +2,7 @@ package crowdsec_bouncer_traefik_plugin //nolint:revive,stylecheck
import (
"context"
httptemplate "http/template"
htmltemplate "html/template"
"net/http"
"net/http/httptest"
"reflect"
@@ -189,11 +189,12 @@ func Test_crowdsecQuery(t *testing.T) {
}
func TestHandleBanServeHTTPWithDifferentMethods(t *testing.T) {
banTemplate := httptemplate.New("html").parse("<html>You are banned</html>")
html := "<html>You are banned</html>"
banTemplate, _ := htmltemplate.New("html").Parse(html)
tests := []struct {
name string
method string
banTemplateString string
banTemplate *htmltemplate.Template
expectBodyContent bool
}{
{
@@ -231,13 +232,13 @@ func TestHandleBanServeHTTPWithDifferentMethods(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bouncer := &Bouncer{
remediationStatusCode: http.StatusForbidden,
remediationStatusCode: http.StatusForbidden,
remediationCustomHeader: "X-Test-Remediation",
banTemplate: tt.banTemplateString,
banTemplate: tt.banTemplate,
}
rw := httptest.NewRecorder()
handleBanServeHTTP(bouncer, rw, tt.method)
handleBanServeHTTP(bouncer, rw, tt.method, "TEST")
// Check status code
if rw.Code != http.StatusForbidden {
@@ -260,8 +261,8 @@ func TestHandleBanServeHTTPWithDifferentMethods(t *testing.T) {
}
// If we expect body content, verify it matches template
if tt.expectBodyContent && body != tt.banTemplateString {
t.Errorf("Expected body %q, got %q", tt.banTemplateString, body)
if tt.expectBodyContent && body != html {
t.Errorf("Expected body %q, got %q", html, body)
}
})
}

View File

@@ -45,3 +45,12 @@ To play the demo environment run:
```bash
make run_custom_ban_page
```
## Another thing to note
In the html of the ban page, you can use {{ .RemediationReason }} that convert on runtime into why the ban page is served.
It's an enum with "APPSEC", "LAPI", "TECHNICAL_ISSUE".
It is useful to help user understand why its request is blocked.
```
<script>var remediation = "{{ .RemediationReason }}"</script>
```
With the above tweak and some other js, you can customize your ban page on runtime.