mirror of
https://github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin.git
synced 2026-07-21 11:38:59 +02:00
✨ add stream mode
This commit is contained in:
+53
-9
@@ -11,7 +11,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -22,13 +21,13 @@ const (
|
|||||||
realIpHeader = "X-Real-Ip"
|
realIpHeader = "X-Real-Ip"
|
||||||
forwardHeader = "X-Forwarded-For"
|
forwardHeader = "X-Forwarded-For"
|
||||||
crowdsecAuthHeader = "X-Api-Key"
|
crowdsecAuthHeader = "X-Api-Key"
|
||||||
crowdsecBouncerRoute = "v1/decisions"
|
crowdsecRoute = "v1/decisions"
|
||||||
crowdsecBouncerStreamRoute = "v1/decisions/stream"
|
crowdsecStreamRoute = "v1/decisions/stream"
|
||||||
cacheBannedValue = "t"
|
cacheBannedValue = "t"
|
||||||
cacheNoBannedValue = "f"
|
cacheNoBannedValue = "f"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ipRegex = regexp.MustCompile(`\b\d+\.\d+\.\d+\.\d+\b`)
|
// var ipRegex = regexp.MustCompile(`\b\d+\.\d+\.\d+\.\d+\b`)
|
||||||
var cache = ttl_map.New()
|
var cache = ttl_map.New()
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -44,7 +43,7 @@ type Config struct {
|
|||||||
func CreateConfig() *Config {
|
func CreateConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
Enabled: false,
|
Enabled: false,
|
||||||
CrowdsecMode: "live",
|
CrowdsecMode: "stream",
|
||||||
CrowdsecLapiScheme: "http",
|
CrowdsecLapiScheme: "http",
|
||||||
CrowdsecLapiHost: "crowdsec:8080",
|
CrowdsecLapiHost: "crowdsec:8080",
|
||||||
CrowdsecLapiKey: "",
|
CrowdsecLapiKey: "",
|
||||||
@@ -59,6 +58,7 @@ type Bouncer struct {
|
|||||||
template *template.Template
|
template *template.Template
|
||||||
|
|
||||||
enabled bool
|
enabled bool
|
||||||
|
crowdsecStreamHealthy bool
|
||||||
crowdsecScheme string
|
crowdsecScheme string
|
||||||
crowdsecHost string
|
crowdsecHost string
|
||||||
crowdsecKey string
|
crowdsecKey string
|
||||||
@@ -90,19 +90,20 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
|
|||||||
testUrl := url.URL{
|
testUrl := url.URL{
|
||||||
Scheme: config.CrowdsecLapiScheme,
|
Scheme: config.CrowdsecLapiScheme,
|
||||||
Host: config.CrowdsecLapiHost,
|
Host: config.CrowdsecLapiHost,
|
||||||
Path: crowdsecBouncerRoute,
|
Path: crowdsecRoute,
|
||||||
}
|
}
|
||||||
_, err := http.NewRequest(http.MethodGet, testUrl.String(), nil)
|
_, err := http.NewRequest(http.MethodGet, testUrl.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("CrowdsecLapiScheme://CrowdsecLapiHost: '%v://%v' must be an URL", config.CrowdsecLapiScheme, config.CrowdsecLapiHost)
|
return nil, fmt.Errorf("CrowdsecLapiScheme://CrowdsecLapiHost: '%v://%v' must be an URL", config.CrowdsecLapiScheme, config.CrowdsecLapiHost)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Bouncer{
|
bouncer := &Bouncer{
|
||||||
next: next,
|
next: next,
|
||||||
name: name,
|
name: name,
|
||||||
template: template.New("CrowdsecBouncer").Delims("[[", "]]"),
|
template: template.New("CrowdsecBouncer").Delims("[[", "]]"),
|
||||||
|
|
||||||
enabled: config.Enabled,
|
enabled: config.Enabled,
|
||||||
|
crowdsecStreamHealthy: false,
|
||||||
crowdsecMode: config.CrowdsecMode,
|
crowdsecMode: config.CrowdsecMode,
|
||||||
crowdsecScheme: config.CrowdsecLapiScheme,
|
crowdsecScheme: config.CrowdsecLapiScheme,
|
||||||
crowdsecHost: config.CrowdsecLapiHost,
|
crowdsecHost: config.CrowdsecLapiHost,
|
||||||
@@ -116,7 +117,9 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
|
|||||||
},
|
},
|
||||||
Timeout: 5 * time.Second,
|
Timeout: 5 * time.Second,
|
||||||
},
|
},
|
||||||
}, nil
|
}
|
||||||
|
go handleStreamCache(bouncer, "true")
|
||||||
|
return bouncer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (a *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
@@ -154,7 +157,7 @@ func (a *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||||||
noneUrl := url.URL{
|
noneUrl := url.URL{
|
||||||
Scheme: a.crowdsecScheme,
|
Scheme: a.crowdsecScheme,
|
||||||
Host: a.crowdsecHost,
|
Host: a.crowdsecHost,
|
||||||
Path: crowdsecBouncerRoute,
|
Path: crowdsecRoute,
|
||||||
RawQuery: fmt.Sprintf("ip=%v&banned=true", remoteHost),
|
RawQuery: fmt.Sprintf("ip=%v&banned=true", remoteHost),
|
||||||
}
|
}
|
||||||
req, _ := http.NewRequest(http.MethodGet, noneUrl.String(), nil)
|
req, _ := http.NewRequest(http.MethodGet, noneUrl.String(), nil)
|
||||||
@@ -258,4 +261,45 @@ func setDecision(clientIP string, isBanned bool, duration int64) {
|
|||||||
} else {
|
} else {
|
||||||
cache.Set(clientIP, cacheNoBannedValue, duration)
|
cache.Set(clientIP, cacheNoBannedValue, duration)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleStreamCache(a *Bouncer, initialized string) {
|
||||||
|
time.AfterFunc(time.Duration(a.updateInterval) * time.Second, func () {
|
||||||
|
handleStreamCache(a, "false")
|
||||||
|
})
|
||||||
|
streamUrl := url.URL{
|
||||||
|
Scheme: a.crowdsecScheme,
|
||||||
|
Host: a.crowdsecHost,
|
||||||
|
Path: crowdsecStreamRoute,
|
||||||
|
RawQuery: fmt.Sprintf("startup=%s", initialized),
|
||||||
|
}
|
||||||
|
req, _ := http.NewRequest(http.MethodGet, streamUrl.String(), nil)
|
||||||
|
req.Header.Add(crowdsecAuthHeader, a.crowdsecKey)
|
||||||
|
res, err := a.client.Do(req)
|
||||||
|
if err != nil || res.StatusCode == http.StatusForbidden {
|
||||||
|
a.crowdsecStreamHealthy = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
a.crowdsecStreamHealthy = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var stream Stream
|
||||||
|
err = json.Unmarshal(body, &stream)
|
||||||
|
if err != nil {
|
||||||
|
a.crowdsecStreamHealthy = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, decision := range stream.New {
|
||||||
|
duration, err := time.ParseDuration(decision.Duration)
|
||||||
|
if err == nil {
|
||||||
|
setDecision(decision.Value, true, int64(duration.Seconds()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, decision := range stream.Deleted {
|
||||||
|
cache.Del(decision.Value)
|
||||||
|
}
|
||||||
|
a.crowdsecStreamHealthy = true
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user