99 "encoding/json"
1010 "errors"
1111 "fmt"
12+ htmltemplate "html/template"
1213 "io"
1314 "net/http"
1415 "net/url"
@@ -106,7 +107,7 @@ type Bouncer struct {
106107 crowdsecStreamRoute string
107108 crowdsecHeader string
108109 redisUnreachableBlock bool
109- banTemplateString string
110+ banTemplate * htmltemplate. Template
110111 clientPoolStrategy * ip.PoolStrategy
111112 serverPoolStrategy * ip.PoolStrategy
112113 httpClient * http.Client
@@ -159,16 +160,9 @@ func New(_ context.Context, next http.Handler, config *configuration.Config, nam
159160 config .CrowdsecLapiKey = apiKey
160161 }
161162
162- var banTemplateString string
163+ var banTemplate * htmltemplate. Template
163164 if config .BanHTMLFilePath != "" {
164- var buf bytes.Buffer
165- banTemplate , _ := configuration .GetHTMLTemplate (config .BanHTMLFilePath )
166- err = banTemplate .Execute (& buf , nil )
167- if err != nil {
168- log .Error ("New:banTemplate is bad formatted " + err .Error ())
169- return nil , err
170- }
171- banTemplateString = buf .String ()
165+ banTemplate , _ = configuration .GetHTMLTemplate (config .BanHTMLFilePath )
172166 }
173167
174168 bouncer := & Bouncer {
@@ -198,7 +192,7 @@ func New(_ context.Context, next http.Handler, config *configuration.Config, nam
198192 defaultDecisionTimeout : config .DefaultDecisionSeconds ,
199193 remediationStatusCode : config .RemediationStatusCode ,
200194 redisUnreachableBlock : config .RedisCacheUnreachableBlock ,
201- banTemplateString : banTemplateString ,
195+ banTemplate : banTemplate ,
202196 crowdsecStreamRoute : crowdsecStreamRoute ,
203197 crowdsecHeader : crowdsecHeader ,
204198 log : log ,
@@ -296,13 +290,13 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
296290 remoteIP , err := ip .GetRemoteIP (req , bouncer .serverPoolStrategy , bouncer .forwardedCustomHeader )
297291 if err != nil {
298292 bouncer .log .Error (fmt .Sprintf ("ServeHTTP:getRemoteIp ip:%s %s" , remoteIP , err .Error ()))
299- handleBanServeHTTP (bouncer , rw , req . Method )
293+ bouncer . handleBanServeHTTP (rw , req , remoteIP , configuration . ReasonTECH )
300294 return
301295 }
302296 isTrusted , err := bouncer .clientPoolStrategy .Checker .Contains (remoteIP )
303297 if err != nil {
304298 bouncer .log .Error (fmt .Sprintf ("ServeHTTP:checkerContains ip:%s %s" , remoteIP , err .Error ()))
305- handleBanServeHTTP (bouncer , rw , req . Method )
299+ bouncer . handleBanServeHTTP (rw , req , remoteIP , configuration . ReasonTECH )
306300 return
307301 }
308302 // if our IP is in the trusted list we bypass the next checks
@@ -313,7 +307,7 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
313307 }
314308
315309 if bouncer .crowdsecMode == configuration .AppsecMode {
316- handleNextServeHTTP (bouncer , remoteIP , rw , req )
310+ bouncer . handleNextServeHTTP (rw , req , remoteIP )
317311 return
318312 }
319313
@@ -325,20 +319,20 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
325319 bouncer .log .Debug (fmt .Sprintf ("ServeHTTP:Get ip:%s isBanned:false %s" , remoteIP , cacheErrString ))
326320 if ! bouncer .redisUnreachableBlock && cacheErrString == cache .CacheUnreachable {
327321 bouncer .log .Error (fmt .Sprintf ("ServeHTTP:Get ip:%s redisUnreachable=true" , remoteIP ))
328- handleNextServeHTTP (bouncer , remoteIP , rw , req )
322+ bouncer . handleNextServeHTTP (rw , req , remoteIP )
329323 return
330324 }
331325 if cacheErrString != cache .CacheMiss {
332326 bouncer .log .Error (fmt .Sprintf ("ServeHTTP:Get ip:%s %s" , remoteIP , cacheErrString ))
333- handleBanServeHTTP (bouncer , rw , req . Method )
327+ bouncer . handleBanServeHTTP (rw , req , remoteIP , configuration . ReasonTECH )
334328 return
335329 }
336330 } else {
337331 bouncer .log .Debug (fmt .Sprintf ("ServeHTTP ip:%s cache:hit isBanned:%v" , remoteIP , value ))
338332 if value == cache .NoBannedValue {
339- handleNextServeHTTP (bouncer , remoteIP , rw , req )
333+ bouncer . handleNextServeHTTP (rw , req , remoteIP )
340334 } else {
341- handleRemediationServeHTTP (bouncer , remoteIP , value , rw , req )
335+ bouncer . handleRemediationServeHTTP (rw , req , remoteIP , value )
342336 }
343337 return
344338 }
@@ -347,18 +341,18 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
347341 // Right here if we cannot join the stream we forbid the request to go on.
348342 if bouncer .crowdsecMode == configuration .StreamMode || bouncer .crowdsecMode == configuration .AloneMode {
349343 if isCrowdsecStreamHealthy {
350- handleNextServeHTTP (bouncer , remoteIP , rw , req )
344+ bouncer . handleNextServeHTTP (rw , req , remoteIP )
351345 } else {
352346 bouncer .log .Debug (fmt .Sprintf ("ServeHTTP isCrowdsecStreamHealthy:false ip:%s updateFailure:%d" , remoteIP , updateFailure ))
353- handleBanServeHTTP (bouncer , rw , req . Method )
347+ bouncer . handleBanServeHTTP (rw , req , remoteIP , configuration . ReasonTECH )
354348 }
355349 } else {
356350 value , err := handleNoStreamCache (bouncer , remoteIP )
357351 if value == cache .NoBannedValue {
358- handleNextServeHTTP (bouncer , remoteIP , rw , req )
352+ bouncer . handleNextServeHTTP (rw , req , remoteIP )
359353 } else {
360354 bouncer .log .Debug (fmt .Sprintf ("ServeHTTP:handleNoStreamCache ip:%s isBanned:%v %s" , remoteIP , value , err .Error ()))
361- handleRemediationServeHTTP (bouncer , remoteIP , value , rw , req )
355+ bouncer . handleRemediationServeHTTP (rw , req , remoteIP , value )
362356 }
363357 }
364358}
@@ -392,48 +386,47 @@ type Login struct {
392386}
393387
394388// To append Headers we need to call rw.WriteHeader after set any header.
395- func handleBanServeHTTP (bouncer * Bouncer , rw http.ResponseWriter , method string ) {
389+ func (bouncer * Bouncer ) handleBanServeHTTP ( rw http.ResponseWriter , req * http. Request , remoteIP , reason string ) {
396390 atomic .AddInt64 (& blockedRequests , 1 )
397391
398392 if bouncer .remediationCustomHeader != "" {
399393 rw .Header ().Set (bouncer .remediationCustomHeader , "ban" )
400394 }
401- if bouncer .banTemplateString == "" {
395+ if bouncer .banTemplate == nil {
402396 rw .WriteHeader (bouncer .remediationStatusCode )
403397 return
404398 }
405399 rw .Header ().Set ("Content-Type" , "text/html; charset=utf-8" )
406400 rw .WriteHeader (bouncer .remediationStatusCode )
407401
408- if method == http .MethodHead {
402+ if req . Method == http .MethodHead {
409403 return
410404 }
411- _ , err := fmt . Fprint (rw , bouncer . banTemplateString )
405+ err := bouncer . banTemplate . Execute (rw , map [ string ] string { "RemediationReason" : reason , "ClientIP" : remoteIP } )
412406 if err != nil {
413- // use warn when https://github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/pull/276 is completed
414- bouncer .log .Error ("handleBanServeHTTP could not write template to ResponseWriter: " + err .Error ())
407+ bouncer .log .Error ("handleBanServeHTTP banTemplateServe " + err .Error ())
415408 }
416409}
417410
418- func handleRemediationServeHTTP (bouncer * Bouncer , remoteIP , remediation string , rw http.ResponseWriter , req * http.Request ) {
411+ func (bouncer * Bouncer ) handleRemediationServeHTTP ( rw http.ResponseWriter , req * http.Request , remoteIP , remediation string ) {
419412 bouncer .log .Debug (fmt .Sprintf ("handleRemediationServeHTTP ip:%s remediation:%s" , remoteIP , remediation ))
420413 if bouncer .captchaClient .Valid && remediation == cache .CaptchaValue && req .Method != http .MethodHead {
421414 if bouncer .captchaClient .Check (remoteIP ) {
422- handleNextServeHTTP (bouncer , remoteIP , rw , req )
415+ bouncer . handleNextServeHTTP (rw , req , remoteIP )
423416 return
424417 }
425418 atomic .AddInt64 (& blockedRequests , 1 ) // If we serve a captcha that should count as a dropped request.
426419 bouncer .captchaClient .ServeHTTP (rw , req , remoteIP )
427420 return
428421 }
429- handleBanServeHTTP (bouncer , rw , req . Method )
422+ bouncer . handleBanServeHTTP (rw , req , remoteIP , configuration . ReasonLAPI )
430423}
431424
432- func handleNextServeHTTP (bouncer * Bouncer , remoteIP string , rw http.ResponseWriter , req * http.Request ) {
425+ func (bouncer * Bouncer ) handleNextServeHTTP ( rw http.ResponseWriter , req * http.Request , remoteIP string ) {
433426 if bouncer .appsecEnabled {
434427 if err := appsecQuery (bouncer , remoteIP , req ); err != nil {
435428 bouncer .log .Debug (fmt .Sprintf ("handleNextServeHTTP ip:%s isWaf:true %s" , remoteIP , err .Error ()))
436- handleBanServeHTTP (bouncer , rw , req . Method )
429+ bouncer . handleBanServeHTTP (rw , req , remoteIP , configuration . ReasonAPPSEC )
437430 return
438431 }
439432 }
0 commit comments