diff --git a/.gitignore b/.gitignore index 68729d6..4c1871f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ .idea/ -.DS_Store \ No newline at end of file +.DS_Store +config +db +logs +docker-compose.dev.yml \ No newline at end of file diff --git a/acquis.yaml b/acquis.yaml index 12f95af..ae7a63f 100644 --- a/acquis.yaml +++ b/acquis.yaml @@ -1,4 +1,4 @@ filenames: - - /var/log/traefik/*.log + - /var/log/traefik/access.log labels: type: traefik \ No newline at end of file diff --git a/bouncer.go b/bouncer.go index 327e316..d480b22 100644 --- a/bouncer.go +++ b/bouncer.go @@ -116,14 +116,17 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h }, cache: ttl_map.New(), } - // if we are on a stream mode, we fetch in a go routine every minute the new decisions. - if config.CrowdsecMode == streamMode { - go handleStreamCache(bouncer, true) - } else if config.CrowdsecMode == aloneMode { - getToken(bouncer) - time.AfterFunc(10*time.Second, func() { - handleStreamCache(bouncer, false) - }) + if config.CrowdsecMode == streamMode || config.CrowdsecMode == aloneMode { + if config.CrowdsecMode == aloneMode { + getToken(bouncer) + } + ticker := time.NewTicker(time.Duration(config.UpdateIntervalSeconds) * time.Second) + go func() { + go handleStreamCache(bouncer) + for range ticker.C { + go handleStreamCache(bouncer) + } + }() } return bouncer, nil } @@ -138,7 +141,7 @@ func (a *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { // TODO Make sur remote address does not include the port. remoteHost, _, err := net.SplitHostPort(req.RemoteAddr) if err != nil { - log.Printf("failed to extract ip from remote address: %v", err) + logger(fmt.Sprintf("failed to extract ip from remote address: %v", err)) a.next.ServeHTTP(rw, req) return } @@ -195,6 +198,10 @@ type Login struct { Expire string `json:"expire"` } +func logger(str string) { + log.Printf("Crowdsec Bouncer Traefik Plugin - %s", str) +} + func contains(source []string, target string) bool { for _, a := range source { if a == target { @@ -217,7 +224,7 @@ func getDecision(cache *ttl_map.Heap, clientIP string) (bool, error) { func setDecision(cache *ttl_map.Heap, clientIP string, isBanned bool, duration int64) { if isBanned { - log.Printf("%v banned", clientIP) + logger(fmt.Sprintf("%v banned", clientIP)) cache.Set(clientIP, cacheBannedValue, duration) } else { cache.Set(clientIP, cacheNoBannedValue, duration) @@ -245,7 +252,7 @@ func handleNoStreamCache(a *Bouncer, rw http.ResponseWriter, req *http.Request, var decisions []Decision err := json.Unmarshal(body, &decisions) if err != nil { - log.Printf("failed to parse body: %s", err) + logger(fmt.Sprintf("failed to parse body: %s", err)) rw.WriteHeader(http.StatusForbidden) return } @@ -259,24 +266,21 @@ func handleNoStreamCache(a *Bouncer, rw http.ResponseWriter, req *http.Request, rw.WriteHeader(http.StatusForbidden) duration, err := time.ParseDuration(decisions[0].Duration) if err != nil { - log.Printf("failed to parse duration: %s", err) + logger(fmt.Sprintf("failed to parse duration: %s", err)) return } setDecision(a.cache, remoteHost, true, int64(duration.Seconds())) } -func handleStreamCache(a *Bouncer, initialized bool) { +func handleStreamCache(a *Bouncer) { // TODO clean properly on exit. - time.AfterFunc(time.Duration(a.updateInterval)*time.Second, func() { - handleStreamCache(a, false) - }) var rawQuery string var path string if a.crowdsecMode == aloneMode { rawQuery = "" path = crowdsecCapiDecisions } else { - rawQuery = fmt.Sprintf("startup=%t", initialized) + rawQuery = fmt.Sprintf("startup=%t", !a.crowdsecStreamHealthy) path = crowdsecLapiStreamRoute } streamRouteURL := url.URL{ @@ -289,7 +293,7 @@ func handleStreamCache(a *Bouncer, initialized bool) { var stream Stream err := json.Unmarshal(body, &stream) if err != nil { - log.Printf("error while parsing body: %s", err) + logger(fmt.Sprintf("error while parsing body: %s", err)) a.crowdsecStreamHealthy = false return } @@ -315,7 +319,7 @@ func getToken(a *Bouncer) { var login Login err := json.Unmarshal(body, &login) if err != nil { - log.Printf("error while parsing body: %s", err) + logger(fmt.Sprintf("error while parsing body: %s", err)) a.crowdsecStreamHealthy = false return } @@ -344,7 +348,7 @@ func crowdsecQuery(a *Bouncer, stringURL string, isPost bool) []byte { } res, err := a.client.Do(req) if err != nil { - log.Printf("error while fetching %v: %s", stringURL, err) + logger(fmt.Sprintf("error while fetching %v: %s", stringURL, err)) a.crowdsecStreamHealthy = false return nil } @@ -358,19 +362,19 @@ func crowdsecQuery(a *Bouncer, stringURL string, isPost bool) []byte { return crowdsecQuery(a, stringURL, false) } if res.StatusCode != http.StatusOK { - log.Printf("error while fetching %v, status code: %d", stringURL, res.StatusCode) + logger(fmt.Sprintf("error while fetching %v, status code: %d", stringURL, res.StatusCode)) a.crowdsecStreamHealthy = false return nil } defer func(body io.ReadCloser) { err = body.Close() if err != nil { - log.Printf("failed to close body reader: %s", err) + logger(fmt.Sprintf("failed to close body reader: %s", err)) } }(res.Body) body, err := ioutil.ReadAll(res.Body) if err != nil { - log.Printf("error while reading body: %s", err) + logger(fmt.Sprintf("error while reading body: %s", err)) a.crowdsecStreamHealthy = false return nil } diff --git a/docker-compose.local.yml b/docker-compose.local.yml index 5b63c13..8c1431e 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -7,7 +7,7 @@ services: command: # - "--log.level=DEBUG" - "--accesslog" - - "--accesslog.filepath=/var/log/traefik/traefik.log" + - "--accesslog.filepath=/var/log/traefik/access.log" - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" @@ -15,28 +15,40 @@ services: - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" volumes: - - "/var/run/docker.sock:/var/run/docker.sock:ro" - - "logs:/var/log/traefik" + - /var/run/docker.sock:/var/run/docker.sock:ro + - logs:/var/log/traefik - ./:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin - ports: - - 8000:80 - - 8080:8080 - depends_on: - - 'crowdsec' - - whoami: - image: traefik/whoami - container_name: "simple-service" labels: - "traefik.enable=true" - - "traefik.http.routers.whoami.rule=Host(`localhost`)" - - "traefik.http.routers.whoami.entrypoints=web" - - "traefik.http.routers.whoami.middlewares=crowdsec@docker" - "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true" - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5" - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseccapilogin=3829a6c9870e4726a377d8951ebb64a1m8psGvMaq1ykJ3zX" - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseccapipwd=Q5pgN8bRNInHGdx6QCksdPOJVLeLQ7ipJntSeuP3r8088zXzRVs4G8liXAKfI1k6" - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseccapiscenarios=crowdsecurity/http-backdoors-attempts,baudneo/zoneminder-bf" + ports: + - 8000:80 + - 8080:8080 + depends_on: + - crowdsec + + whoami1: + image: traefik/whoami + container_name: "simple-service1" + labels: + - "traefik.enable=true" + - "traefik.http.routers.whoami.rule=Host(`localhost`)" + - "traefik.http.routers.whoami.entrypoints=web" + - "traefik.http.routers.whoami.middlewares=crowdsec@docker" + - "traefik.http.services.whoami.loadbalancer.server.port=80" + whoami2: + image: traefik/whoami + container_name: "simple-service2" + labels: + - "traefik.enable=true" + - "traefik.http.routers.whoami.rule=Host(`localhost`)" + - "traefik.http.routers.whoami.entrypoints=web" + - "traefik.http.routers.whoami.middlewares=crowdsec@docker" + - "traefik.http.services.whoami.loadbalancer.server.port=80" crowdsec: image: crowdsecurity/crowdsec:v1.4.1 @@ -49,8 +61,8 @@ services: volumes: - ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro - logs:/var/log/traefik:ro - - crowdsec-db:/var/lib/crowdsec/data/ - - crowdsec-config:/etc/crowdsec/ + - db:/var/lib/crowdsec/data/ + - config:/etc/crowdsec/ volumes: logs: diff --git a/docker-compose.yml b/docker-compose.yml index 0e61236..14e0e69 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: command: # - "--log.level=DEBUG" - "--accesslog" - - "--accesslog.filepath=/var/log/traefik/traefik.log" + - "--accesslog.filepath=/var/log/traefik/access.log" - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false"