Compare commits

..
5 Commits
Author SHA1 Message Date
maxlerebourgandmax.lerebourg 70ad0365f0 🐛 fix content-type header for ban and captcha page (#166)
* 🍱 fix content-type header for ban and captcha page

* 🍱 Add comment to warn future developer

---------

Co-authored-by: max.lerebourg <max.lerebourg@monisnap.com>
2024-05-16 18:40:28 +02:00
mathieuHa 8975216af8 📝 Update readme on loglevel written to stdout/stderr (#164) 2024-05-16 09:32:14 +02:00
mathieuHa e6b10b8ac4 ⬆️ 📝 Update exemples and version traefik 3, crowdsec 1.6.1-2, plugin … (#160)
* ⬆️ 📝 Update exemples and version traefik 3, crowdsec 1.6.1-2, plugin 1.3.0

* 📝 🐛 Fix race in exemple and doc for tls
2024-05-01 20:03:19 +02:00
mathieuHa 1f11531509 📝 Update doc for appsec with required collection and upgrade images (#158) 2024-05-01 19:50:00 +02:00
mathieuHaandmax.lerebourg ee97250acf Add grace period to reach LAPI without blocking further queries (#153)
*  Add grace period to reach LAPI without blocking further queries

* 🐛 Fix config validation for maxFailedStreamUpdate

* 🚨 Fix some lint issue

* 🚨 Bypass lint complexity on ServeHTTP

* 🍱 fix and improve

* 🚨 Fix lint

* 🚨 Fix lint

* 🐛 Fix logic for update max failure

* 📝 Update doc and docker compose local reset

* 🍱 fix log nightmare

* 🍱 fix

---------

Co-authored-by: max.lerebourg <max.lerebourg@monisnap.com>
2024-05-01 18:07:44 +02:00
25 changed files with 134 additions and 104 deletions
+27 -16
View File
@@ -26,25 +26,25 @@ run_local:
docker compose -f docker-compose.local.yml up -d --remove-orphans docker compose -f docker-compose.local.yml up -d --remove-orphans
run_behindproxy: run_behindproxy:
docker compose -f examples/behind-proxy/docker-compose.cloudflare.yml up -d --remove-orphans docker compose -f examples/behind-proxy/docker-compose.yml up -d --remove-orphans
run_cacheredis: run_cacheredis:
docker compose -f examples/redis-cache/docker-compose.redis.yml up -d --remove-orphans docker compose -f examples/redis-cache/docker-compose.yml up -d --remove-orphans
run_trustedips: run_trustedips:
docker compose -f examples/trusted-ips/docker-compose.trusted.yml up -d --remove-orphans docker compose -f examples/trusted-ips/docker-compose.yml up -d --remove-orphans
run_binaryvm: run_binaryvm:
cd examples/binary-vm/ && sudo vagrant up cd examples/binary-vm/ && sudo vagrant up
run_tlsauth: run_tlsauth:
docker compose -f examples/tls-auth/docker-compose.tls-auth.yml down && docker compose -f examples/tls-auth/docker-compose.tls-auth.yml up -d && docker compose -f examples/tls-auth/docker-compose.tls-auth.yml restart && docker compose -f examples/tls-auth/docker-compose.tls-auth.yml logs -f docker compose -f examples/tls-auth/docker-compose.yml up -d --remove-orphans
run_appsec: run_appsec:
docker compose -f examples/appsec-enabled/docker-compose.appsec-enabled.yml up -d docker compose -f examples/appsec-enabled/docker-compose.yml up -d
run_captcha: run_captcha:
docker compose -f examples/captcha/docker-compose.captcha.yml up -d docker compose -f examples/captcha/docker-compose.yml up -d
run_custom_ban_page: run_custom_ban_page:
docker compose -f examples/custom-ban-page/docker-compose.yml up -d docker compose -f examples/custom-ban-page/docker-compose.yml up -d
@@ -62,19 +62,25 @@ restart:
docker compose -f docker-compose.yml restart docker compose -f docker-compose.yml restart
restart_behindproxy: restart_behindproxy:
docker compose -f examples/behind-proxy/docker-compose.cloudflare.yml restart docker compose -f examples/behind-proxy/docker-compose.yml restart
restart_cacheredis: restart_cacheredis:
docker compose -f examples/redis-cache/docker-compose.redis.yml restart docker compose -f examples/redis-cache/docker-compose.yml restart
restart_trustedips: restart_trustedips:
docker compose -f examples/trusted-ips/docker-compose.trusted.yml restart docker compose -f examples/trusted-ips/docker-compose.yml restart
restart_tlsauth: restart_tlsauth:
docker compose -f examples/tls-auth/docker-compose.tls-auth.yml docker compose -f examples/tls-auth/docker-compose.yml
restart_appsec: restart_appsec:
docker compose -f examples/tls-auth/docker-compose.appsec-enabled.yml docker compose -f examples/tls-auth/docker-compose.yml
restart_captcha:
docker compose -f examples/captcha/docker-compose.yml
restart_custombanpage:
docker compose -f examples/custom-ban-page/docker-compose.yml
show_logs: show_logs:
docker compose -f docker-compose.yml restart docker compose -f docker-compose.yml restart
@@ -86,11 +92,13 @@ show_dev_logs:
docker compose -f docker-compose.dev.yml logs -f docker compose -f docker-compose.dev.yml logs -f
clean_all_docker: clean_all_docker:
docker compose -f examples/behind-proxy/docker-compose.cloudflare.yml down --remove-orphans docker compose -f examples/behind-proxy/docker-compose.yml down --remove-orphans
docker compose -f examples/redis-cache/docker-compose.redis.yml down --remove-orphans docker compose -f examples/redis-cache/docker-compose.yml down --remove-orphans
docker compose -f examples/trusted-ips/docker-compose.trusted.yml down --remove-orphans docker compose -f examples/trusted-ips/docker-compose.yml down --remove-orphans
docker compose -f examples/tls-auth/docker-compose.tls-auth.yml down --remove-orphans docker compose -f examples/tls-auth/docker-compose.yml down --remove-orphans
docker compose -f examples/appsec-enabled/docker-compose.appsec-enabled.yml down --remove-orphans docker compose -f examples/appsec-enabled/docker-compose.yml down --remove-orphans
docker compose -f examples/captcha/docker-compose.yml down --remove-orphans
docker compose -f examples/custom-ban-page/docker-compose.yml down --remove-orphans
docker compose -f docker-compose.local.yml down --remove-orphans docker compose -f docker-compose.local.yml down --remove-orphans
docker compose -f docker-compose.yml down --remove-orphans docker compose -f docker-compose.yml down --remove-orphans
@@ -101,3 +109,6 @@ clean_vagrant:
show_metrics: show_metrics:
docker exec crowdsec cscli metrics docker exec crowdsec cscli metrics
show_decisions:
docker exec crowdsec cscli decisions list
+6 -1
View File
@@ -317,7 +317,7 @@ Only one instance of the plugin is *possible*.
- Enable the plugin - Enable the plugin
- LogLevel - LogLevel
- string - string
- default: `INFO`, expected values are: `INFO`, `DEBUG` - default: `INFO`, expected values are: `INFO`, `DEBUG`, `ERROR`, log are written to `stdout` / `stderr`
- CrowdsecMode - CrowdsecMode
- string - string
- default: `live`, expected values are: `none`, `live`, `stream`, `alone`, `appsec` - default: `live`, expected values are: `none`, `live`, `stream`, `alone`, `appsec`
@@ -396,6 +396,10 @@ Only one instance of the plugin is *possible*.
- int64 - int64
- default: 60 - default: 60
- Used only in `stream` mode, the interval between requests to fetch blacklisted IPs from LAPI - Used only in `stream` mode, the interval between requests to fetch blacklisted IPs from LAPI
- UpdateMaxFailure
- int64
- default: 0
- Used only in `stream` and `alone` mode, the maximum number of time we can not reach Crowdsec before blocking traffic (set -1 to never block)
- DefaultDecisionSeconds - DefaultDecisionSeconds
- int64 - int64
- default: 60 - default: 60
@@ -475,6 +479,7 @@ http:
enabled: false enabled: false
logLevel: DEBUG logLevel: DEBUG
updateIntervalSeconds: 60 updateIntervalSeconds: 60
updateMaxFailure: 0
defaultDecisionSeconds: 60 defaultDecisionSeconds: 60
httpTimeoutSeconds: 10 httpTimeoutSeconds: 10
crowdsecMode: live crowdsecMode: live
+18 -9
View File
@@ -43,6 +43,7 @@ const (
var ( var (
isStartup = true isStartup = true
isCrowdsecStreamHealthy = true isCrowdsecStreamHealthy = true
updateFailure = 0
ticker chan bool ticker chan bool
) )
@@ -69,6 +70,7 @@ type Bouncer struct {
crowdsecPassword string crowdsecPassword string
crowdsecScenarios []string crowdsecScenarios []string
updateInterval int64 updateInterval int64
updateMaxFailure int
defaultDecisionTimeout int64 defaultDecisionTimeout int64
customHeader string customHeader string
crowdsecStreamRoute string crowdsecStreamRoute string
@@ -150,6 +152,7 @@ func New(ctx context.Context, next http.Handler, config *configuration.Config, n
crowdsecPassword: config.CrowdsecCapiPassword, crowdsecPassword: config.CrowdsecCapiPassword,
crowdsecScenarios: config.CrowdsecCapiScenarios, crowdsecScenarios: config.CrowdsecCapiScenarios,
updateInterval: config.UpdateIntervalSeconds, updateInterval: config.UpdateIntervalSeconds,
updateMaxFailure: config.UpdateMaxFailure,
customHeader: config.ForwardedHeadersCustomName, customHeader: config.ForwardedHeadersCustomName,
defaultDecisionTimeout: config.DefaultDecisionSeconds, defaultDecisionTimeout: config.DefaultDecisionSeconds,
banTemplateString: banTemplateString, banTemplateString: banTemplateString,
@@ -282,7 +285,7 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if isCrowdsecStreamHealthy { if isCrowdsecStreamHealthy {
handleNextServeHTTP(bouncer, remoteIP, rw, req) handleNextServeHTTP(bouncer, remoteIP, rw, req)
} else { } else {
bouncer.log.Debug(fmt.Sprintf("ServeHTTP isCrowdsecStreamHealthy:false ip:%s", remoteIP)) bouncer.log.Debug(fmt.Sprintf("ServeHTTP isCrowdsecStreamHealthy:false ip:%s updateFailure:%d", remoteIP, updateFailure))
handleBanServeHTTP(bouncer, rw) handleBanServeHTTP(bouncer, rw)
} }
} else { } else {
@@ -324,12 +327,15 @@ type Login struct {
Expire string `json:"expire"` Expire string `json:"expire"`
} }
// To append Headers we need to call rw.WriteHeader after set any header.
func handleBanServeHTTP(bouncer *Bouncer, rw http.ResponseWriter) { func handleBanServeHTTP(bouncer *Bouncer, rw http.ResponseWriter) {
rw.WriteHeader(http.StatusForbidden) if bouncer.banTemplateString == "" {
if bouncer.banTemplateString != "" { rw.WriteHeader(http.StatusForbidden)
rw.Header().Set("Content-Type", "text/html; charset=utf-8") return
fmt.Fprint(rw, bouncer.banTemplateString)
} }
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
rw.WriteHeader(http.StatusForbidden)
fmt.Fprint(rw, bouncer.banTemplateString)
} }
func handleRemediationServeHTTP(bouncer *Bouncer, remoteIP, remediation string, rw http.ResponseWriter, req *http.Request) { func handleRemediationServeHTTP(bouncer *Bouncer, remoteIP, remediation string, rw http.ResponseWriter, req *http.Request) {
@@ -358,10 +364,15 @@ func handleNextServeHTTP(bouncer *Bouncer, remoteIP string, rw http.ResponseWrit
func handleStreamTicker(bouncer *Bouncer) { func handleStreamTicker(bouncer *Bouncer) {
if err := handleStreamCache(bouncer); err != nil { if err := handleStreamCache(bouncer); err != nil {
isCrowdsecStreamHealthy = false bouncer.log.Debug(fmt.Sprintf("handleStreamTicker updateFailure:%d isCrowdsecStreamHealthy:%t %s", updateFailure, isCrowdsecStreamHealthy, err.Error()))
bouncer.log.Error(err.Error()) if bouncer.updateMaxFailure != -1 && updateFailure >= bouncer.updateMaxFailure && isCrowdsecStreamHealthy {
isCrowdsecStreamHealthy = false
bouncer.log.Error(fmt.Sprintf("handleStreamTicker:error updateFailure:%d %s", updateFailure, err.Error()))
}
updateFailure++
} else { } else {
isCrowdsecStreamHealthy = true isCrowdsecStreamHealthy = true
updateFailure = 0
} }
} }
@@ -457,7 +468,6 @@ func getToken(bouncer *Bouncer) error {
var login Login var login Login
err = json.Unmarshal(body, &login) err = json.Unmarshal(body, &login)
if err != nil { if err != nil {
isCrowdsecStreamHealthy = false
return fmt.Errorf("getToken:parsingBody %w", err) return fmt.Errorf("getToken:parsingBody %w", err)
} }
if login.Code == 200 && len(login.Token) > 0 { if login.Code == 200 && len(login.Token) > 0 {
@@ -516,7 +526,6 @@ func handleStreamCache(bouncer *Bouncer) error {
bouncer.cacheClient.Delete(decision.Value) bouncer.cacheClient.Delete(decision.Value)
} }
bouncer.log.Debug("handleStreamCache:updated") bouncer.log.Debug("handleStreamCache:updated")
isCrowdsecStreamHealthy = true
return nil return nil
} }
+4 -3
View File
@@ -1,6 +1,6 @@
services: services:
traefik: traefik:
image: "traefik:v2.11.0" image: "traefik:v3.0.0"
container_name: "traefik" container_name: "traefik"
restart: unless-stopped restart: unless-stopped
command: command:
@@ -49,14 +49,15 @@ services:
- "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true" - "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true"
- "traefik.http.middlewares.crowdsec.plugin.bouncer.loglevel=DEBUG" - "traefik.http.middlewares.crowdsec.plugin.bouncer.loglevel=DEBUG"
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecappsecenabled=true" - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecappsecenabled=true"
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecmode=stream"
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5=" - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5="
crowdsec: crowdsec:
image: crowdsecurity/crowdsec:v1.6.0 image: crowdsecurity/crowdsec:v1.6.1-2
container_name: "crowdsec" container_name: "crowdsec"
restart: unless-stopped restart: unless-stopped
environment: environment:
COLLECTIONS: crowdsecurity/traefik crowdsecurity/appsec-virtual-patching COLLECTIONS: crowdsecurity/traefik crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules
CUSTOM_HOSTNAME: crowdsec CUSTOM_HOSTNAME: crowdsec
BOUNCER_KEY_TRAEFIK: 40796d93c2958f9e58345514e67740e5= BOUNCER_KEY_TRAEFIK: 40796d93c2958f9e58345514e67740e5=
volumes: volumes:
+4 -4
View File
@@ -1,6 +1,6 @@
services: services:
traefik: traefik:
image: "traefik:v2.11.0" image: "traefik:v3.0.0"
container_name: "traefik" container_name: "traefik"
restart: unless-stopped restart: unless-stopped
command: command:
@@ -12,7 +12,7 @@ services:
- "--entrypoints.web.address=:80" - "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.3.0-beta3" - "--experimental.plugins.bouncer.version=v1.3.0"
volumes: volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro" - "/var/run/docker.sock:/var/run/docker.sock:ro"
# - './ban.html:/ban.html:ro' # - './ban.html:/ban.html:ro'
@@ -59,11 +59,11 @@ services:
- "traefik.http.middlewares.crowdsec.plugin.bouncer.forwardedheaderstrustedips=172.21.0.5" - "traefik.http.middlewares.crowdsec.plugin.bouncer.forwardedheaderstrustedips=172.21.0.5"
crowdsec: crowdsec:
image: crowdsecurity/crowdsec:v1.6.0 image: crowdsecurity/crowdsec:v1.6.1-2
container_name: "crowdsec" container_name: "crowdsec"
restart: unless-stopped restart: unless-stopped
environment: environment:
COLLECTIONS: crowdsecurity/traefik COLLECTIONS: crowdsecurity/traefik crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules
CUSTOM_HOSTNAME: crowdsec CUSTOM_HOSTNAME: crowdsec
# We need to register one api key per service we will use # We need to register one api key per service we will use
BOUNCER_KEY_TRAEFIK: FIXME-LAPI-KEY-1= BOUNCER_KEY_TRAEFIK: FIXME-LAPI-KEY-1=
@@ -2,7 +2,7 @@ version: "3.8"
services: services:
traefik: traefik:
image: "traefik:v2.10.7" image: "traefik:v3.0.0"
container_name: "traefik" container_name: "traefik"
restart: unless-stopped restart: unless-stopped
command: command:
@@ -15,12 +15,12 @@ services:
- "--entrypoints.web.address=:80" - "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.2.0" - "--experimental.plugins.bouncer.version=v1.3.0"
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" # - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
- logs-appsec-enabled:/var/log/traefik - logs-appsec-enabled:/var/log/traefik
- ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin # - ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
ports: ports:
- 8000:80 - 8000:80
- 8080:8080 - 8080:8080
@@ -51,11 +51,11 @@ services:
crowdsec: crowdsec:
image: crowdsecurity/crowdsec:v1.6.0 image: crowdsecurity/crowdsec:v1.6.1-2
container_name: "crowdsec" container_name: "crowdsec"
restart: unless-stopped restart: unless-stopped
environment: environment:
COLLECTIONS: crowdsecurity/traefik COLLECTIONS: crowdsecurity/traefik crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules
CUSTOM_HOSTNAME: crowdsec CUSTOM_HOSTNAME: crowdsec
BOUNCER_KEY_TRAEFIK_DEV: 40796d93c2958f9e58345514e67740e5 BOUNCER_KEY_TRAEFIK_DEV: 40796d93c2958f9e58345514e67740e5
volumes: volumes:
@@ -1,8 +1,6 @@
version: "3.8"
services: services:
cloudflare: cloudflare:
image: "traefik:v2.10.4" image: "traefik:v3.0.0"
container_name: "cloudflare" container_name: "cloudflare"
restart: unless-stopped restart: unless-stopped
command: command:
@@ -21,7 +19,7 @@ services:
- 8080:8080 - 8080:8080
traefik: traefik:
image: "traefik:v2.10.4" image: "traefik:v3.0.0"
container_name: "traefik" container_name: "traefik"
restart: unless-stopped restart: unless-stopped
command: command:
@@ -35,7 +33,7 @@ services:
- "--entrypoints.web.forwardedheaders.trustedips=172.21.0.5" - "--entrypoints.web.forwardedheaders.trustedips=172.21.0.5"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.1.13" - "--experimental.plugins.bouncer.version=v1.3.0"
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
- logs-traefik:/var/log/traefik - logs-traefik:/var/log/traefik
@@ -47,7 +45,7 @@ services:
whoami-foo: whoami-foo:
image: traefik/whoami image: traefik/whoami
container_name: "simple-service-foo" container_name: "simple-service-foo-cloudflare"
restart: unless-stopped restart: unless-stopped
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
@@ -66,7 +64,7 @@ services:
whoami-bar: whoami-bar:
image: traefik/whoami image: traefik/whoami
container_name: "simple-service-bar" container_name: "simple-service-bar-cloudflare"
restart: unless-stopped restart: unless-stopped
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
@@ -81,7 +79,7 @@ services:
crowdsec: crowdsec:
image: crowdsecurity/crowdsec:v1.5.2 image: crowdsecurity/crowdsec:v1.6.1-2
container_name: "crowdsec" container_name: "crowdsec"
restart: unless-stopped restart: unless-stopped
environment: environment:
@@ -2,7 +2,7 @@
DEBIAN_FRONTEND=noninteractive sudo apt-get update && sudo apt-get install wget -y DEBIAN_FRONTEND=noninteractive sudo apt-get update && sudo apt-get install wget -y
# DEBIAN_FRONTEND=noninteractive sudo apt-get upgrade -y --assume-yes # DEBIAN_FRONTEND=noninteractive sudo apt-get upgrade -y --assume-yes
wget -O traefik.tar.gz "https://github.com/traefik/traefik/releases/download/v2.10.4/traefik_v2.10.4_linux_amd64.tar.gz" wget -O traefik.tar.gz "https://github.com/traefik/traefik/releases/download/v3.0.0/traefik_v3.0.0_linux_amd64.tar.gz"
tar -zxvf traefik.tar.gz tar -zxvf traefik.tar.gz
# inspired from https://gist.github.com/ubergesundheit/7c9d875befc2d7bfd0bf43d8b3862d85 # inspired from https://gist.github.com/ubergesundheit/7c9d875befc2d7bfd0bf43d8b3862d85
sudo mv ./traefik /usr/local/bin/ sudo mv ./traefik /usr/local/bin/
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
DEBIAN_FRONTEND=noninteractive sudo apt-get update && apt-get install wget -y DEBIAN_FRONTEND=noninteractive sudo apt-get update && apt-get install wget -y
wget -O whoami.tar.gz "https://github.com/traefik/whoami/releases/download/v1.9.0/whoami_v1.9.0_linux_amd64.tar.gz" wget -O whoami.tar.gz "https://github.com/traefik/whoami/releases/download/v1.10.1/whoami_v1.10.1_linux_amd64.tar.gz"
tar -zxvf whoami.tar.gz tar -zxvf whoami.tar.gz
# inspired from https://gist.github.com/ubergesundheit/7c9d875befc2d7bfd0bf43d8b3862d85 # inspired from https://gist.github.com/ubergesundheit/7c9d875befc2d7bfd0bf43d8b3862d85
sudo mv ./whoami /usr/local/bin/ sudo mv ./whoami /usr/local/bin/
+2 -2
View File
@@ -32,7 +32,7 @@ It is not directly accessible from Traefik even when importing the plugin, so [d
```yaml ```yaml
... ...
traefik: traefik:
image: "traefik:v2.11.0" image: "traefik:v3.0.0"
volumes: volumes:
- './captcha.html:/captcha.html' - './captcha.html:/captcha.html'
... ...
@@ -52,7 +52,7 @@ More information is available on configuring Crowdsec in the [official documenta
```yaml ```yaml
... ...
crowdsec: crowdsec:
image: crowdsecurity/crowdsec:v1.6.0 image: crowdsecurity/crowdsec:v1.6.1-2
volumes: volumes:
# For captcha and ban mixed decision # For captcha and ban mixed decision
- './profiles.yaml:/etc/crowdsec/profiles.yaml:ro' - './profiles.yaml:/etc/crowdsec/profiles.yaml:ro'
@@ -1,6 +1,6 @@
services: services:
traefik: traefik:
image: "traefik:v2.11.0" image: "traefik:v3.0.0"
container_name: "traefik" container_name: "traefik"
restart: unless-stopped restart: unless-stopped
command: command:
@@ -12,14 +12,14 @@ services:
- "--providers.docker.exposedbydefault=false" - "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80" - "--entrypoints.web.address=:80"
# - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
# - "--experimental.plugins.bouncer.version=v1.2.0" - "--experimental.plugins.bouncer.version=v1.3.0"
- "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" # - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
- logs-captcha-enabled:/var/log/traefik - logs-captcha-enabled:/var/log/traefik
- './captcha.html:/captcha.html' - './captcha.html:/captcha.html'
- ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin # - ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
ports: ports:
- 8000:80 - 8000:80
- 8080:8080 - 8080:8080
@@ -55,7 +55,7 @@ services:
- "traefik.http.middlewares.crowdsec.plugin.bouncer.captchaHTMLFilePath=/captcha.html" - "traefik.http.middlewares.crowdsec.plugin.bouncer.captchaHTMLFilePath=/captcha.html"
crowdsec: crowdsec:
image: crowdsecurity/crowdsec:v1.6.0 image: crowdsecurity/crowdsec:v1.6.1-2
container_name: "crowdsec" container_name: "crowdsec"
restart: unless-stopped restart: unless-stopped
environment: environment:
+6 -6
View File
@@ -1,6 +1,6 @@
services: services:
traefik: traefik:
image: "traefik:v2.11.0" image: "traefik:v3.0.0"
container_name: "traefik" container_name: "traefik"
restart: unless-stopped restart: unless-stopped
command: command:
@@ -12,14 +12,14 @@ services:
- "--providers.docker.exposedbydefault=false" - "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80" - "--entrypoints.web.address=:80"
# - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
# - "--experimental.plugins.bouncer.version=v1.3.0-beta3" - "--experimental.plugins.bouncer.version=v1.3.0"
- "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" # - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
- logs-custom-ban-page-enabled:/var/log/traefik - logs-custom-ban-page-enabled:/var/log/traefik
- './ban.html:/ban.html' - './ban.html:/ban.html'
- ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin # - ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
ports: ports:
- 8000:80 - 8000:80
- 8080:8080 - 8080:8080
@@ -46,7 +46,7 @@ services:
- "traefik.http.middlewares.crowdsec.plugin.bouncer.banHtmlFilePath=/ban.html" - "traefik.http.middlewares.crowdsec.plugin.bouncer.banHtmlFilePath=/ban.html"
crowdsec: crowdsec:
image: crowdsecurity/crowdsec:v1.6.0 image: crowdsecurity/crowdsec:v1.6.1-2
container_name: "crowdsec" container_name: "crowdsec"
restart: unless-stopped restart: unless-stopped
environment: environment:
+3 -1
View File
@@ -32,13 +32,15 @@ minikube start
helm repo add traefik https://traefik.github.io/charts helm repo add traefik https://traefik.github.io/charts
helm repo update helm repo update
kubectl create ns traefik kubectl create ns traefik
helm upgrade --version v26.1.0 --install --namespace=traefik \ helm upgrade --version v28.0.0 --install --namespace=traefik \
--values=./traefik/values.yml \ --values=./traefik/values.yml \
traefik traefik/traefik traefik traefik/traefik
``` ```
_A [bug](https://github.com/traefik/traefik-helm-chart/commit/e7ce1b410c858642069033305eb6362f26689f16) has been fixed in chart 26.1.0 that could prevent plugin to be loaded_ _A [bug](https://github.com/traefik/traefik-helm-chart/commit/e7ce1b410c858642069033305eb6362f26689f16) has been fixed in chart 26.1.0 that could prevent plugin to be loaded_
_v28.0.0 of the Traefik helm chart is only compatible with v3 of Traefik_
#### View the Traefik dashboard #### View the Traefik dashboard
> Port forward the dashboard: > Port forward the dashboard:
+1 -1
View File
@@ -1,5 +1,5 @@
image: image:
tag: v1.6.0 tag: v1.6.1-2
agent: agent:
acquisition: acquisition:
+1 -1
View File
@@ -1,5 +1,5 @@
# dashboard.yaml # dashboard.yaml
apiVersion: traefik.containo.us/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:
name: dashboard name: dashboard
+1 -1
View File
@@ -1,4 +1,4 @@
apiVersion: traefik.containo.us/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: Middleware kind: Middleware
metadata: metadata:
name: bouncer name: bouncer
+2 -2
View File
@@ -1,5 +1,5 @@
image: image:
tag: v2.11 tag: v3.0.0
logs: logs:
general: general:
@@ -14,4 +14,4 @@ experimental:
plugins: plugins:
bouncer: bouncer:
moduleName: "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" moduleName: "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
version: "v1.2.0" version: "v1.3.0"
@@ -1,8 +1,6 @@
version: "3.8"
services: services:
traefik: traefik:
image: "traefik:v2.10.4" image: "traefik:v3.0.0"
container_name: "traefik" container_name: "traefik"
restart: unless-stopped restart: unless-stopped
command: command:
@@ -15,7 +13,7 @@ services:
- "--entrypoints.web.address=:80" - "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.1.13" - "--experimental.plugins.bouncer.version=v1.3.0"
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" # - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
@@ -52,7 +50,7 @@ services:
whoami-redis-secure: whoami-redis-secure:
image: traefik/whoami image: traefik/whoami
container_name: "simple-service-bar" container_name: "simple-service-bar-redis"
restart: unless-stopped restart: unless-stopped
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
@@ -73,7 +71,7 @@ services:
crowdsec: crowdsec:
image: crowdsecurity/crowdsec:v1.5.2 image: crowdsecurity/crowdsec:v1.6.1-2
container_name: "crowdsec" container_name: "crowdsec"
restart: unless-stopped restart: unless-stopped
environment: environment:
@@ -1,8 +1,6 @@
version: "3.8"
services: services:
traefik: traefik:
image: "traefik:v2.10.4" image: "traefik:v3.0.0"
container_name: "traefik" container_name: "traefik"
restart: unless-stopped restart: unless-stopped
command: command:
@@ -15,16 +13,18 @@ services:
- "--entrypoints.web.address=:80" - "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.1.13" - "--experimental.plugins.bouncer.version=v1.3.0"
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
# - ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
ports: ports:
- 80:80 - 80:80
- 8080:8080 - 8080:8080
whoami-foo: whoami-foo:
image: traefik/whoami image: traefik/whoami
container_name: "simple-service-foo" container_name: "simple-service-foo-alone"
restart: unless-stopped restart: unless-stopped
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
@@ -37,9 +37,7 @@ services:
# - "traefik.http.middlewares.crowdsec.plugin.bouncer.loglevel=DEBUG" # - "traefik.http.middlewares.crowdsec.plugin.bouncer.loglevel=DEBUG"
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecmode=alone" - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecmode=alone"
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5" - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
- "traefik.http.middlewares.crowdsec.plugin.bouncer.CrowdsecCapiMachineId=logincacacalfkrjebfreifgzfblezgyfoerxsqxsqxsqxsr" - "traefik.http.middlewares.crowdsec.plugin.bouncer.CrowdsecCapiMachineId=FIXME"
- "traefik.http.middlewares.crowdsec.plugin.bouncer.CrowdsecCapiPassword=Password2" - "traefik.http.middlewares.crowdsec.plugin.bouncer.CrowdsecCapiPassword=FIXME"
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseccapiscenarios=crowdsecurity/sshd,crowdsecurity/asterisk_bf,crowdsecurity/asterisk_user_enum,crowdsecurity/base-http-scenarios" - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseccapiscenarios=crowdsecurity/sshd,crowdsecurity/asterisk_bf,crowdsecurity/asterisk_user_enum,crowdsecurity/base-http-scenarios"
volumes:
logs-local:
+1 -1
View File
@@ -1,4 +1,4 @@
FROM ubuntu:22.04 FROM ubuntu:24.04
RUN apt-get update && apt-get install -y curl wget RUN apt-get update && apt-get install -y curl wget
RUN VERSION=$(curl --silent "https://api.github.com/repos/cloudflare/cfssl/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') && VNUMBER=${VERSION#"v"} && wget https://github.com/cloudflare/cfssl/releases/download/${VERSION}/cfssl_${VNUMBER}_linux_amd64 -O cfssl && chmod +x cfssl && mv cfssl /usr/local/bin RUN VERSION=$(curl --silent "https://api.github.com/repos/cloudflare/cfssl/releases/latest" | grep '"tag_name"' | sed -E 's/.*"([^"]+)".*/\1/') && VNUMBER=${VERSION#"v"} && wget https://github.com/cloudflare/cfssl/releases/download/${VERSION}/cfssl_${VNUMBER}_linux_amd64 -O cfssl && chmod +x cfssl && mv cfssl /usr/local/bin
+3 -4
View File
@@ -72,11 +72,11 @@ The service `whoami-bar` will authenticate with a **client certificate** signed
Access to a route that communicate via https and authenticate with API-key: Access to a route that communicate via https and authenticate with API-key:
``` ```
curl http://localhost:80/foo curl http://localhost:8000/foo
``` ```
Access to a route that communicate via https and authenticate with a client certificate: Access to a route that communicate via https and authenticate with a client certificate:
``` ```
curl http://localhost:80/bar curl http://localhost:8000/bar
``` ```
Access to the traefik dashboard Access to the traefik dashboard
``` ```
@@ -89,5 +89,4 @@ make run_tlsauth
``` ```
Note: Note:
> This example is still in Beta and use a new version of Crowdsec (v1.4.3) at time of writing > Traefik need to be restart if certificates are regenerated after his launch
A functionnality has been disabled in Crowdsec in order to make the example work DISABLE_AGENT: "true"
@@ -1,8 +1,6 @@
version: "3.8"
services: services:
traefik: traefik:
image: "traefik:v2.10.4" image: "traefik:v3.0.0"
container_name: "traefik" container_name: "traefik"
restart: unless-stopped restart: unless-stopped
command: command:
@@ -15,7 +13,7 @@ services:
- "--entrypoints.web.address=:80" - "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.1.13" - "--experimental.plugins.bouncer.version=v1.3.0"
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" # - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
@@ -25,10 +23,11 @@ services:
# - ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin # - ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
ports: ports:
- 80:80 - 8000:80
- 8080:8080 - 8080:8080
depends_on: depends_on:
- crowdsec - crowdsec
- gencert
# Use HTTPS scheme but with lapikey authentication # Use HTTPS scheme but with lapikey authentication
# whoami-foo: # whoami-foo:
@@ -66,7 +65,7 @@ services:
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecLapiTLSCertificateBouncerKeyFile=/etc/traefik/crowdsec-certs/bouncer-key.pem" - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecLapiTLSCertificateBouncerKeyFile=/etc/traefik/crowdsec-certs/bouncer-key.pem"
crowdsec: crowdsec:
image: crowdsecurity/crowdsec:v1.5.2 image: crowdsecurity/crowdsec:v1.6.1-2
container_name: "crowdsec" container_name: "crowdsec"
restart: unless-stopped restart: unless-stopped
environment: environment:
@@ -80,11 +79,13 @@ services:
CERT_FILE: "/etc/crowdsec/certs/server.pem" CERT_FILE: "/etc/crowdsec/certs/server.pem"
KEY_FILE: "/etc/crowdsec/certs/server-key.pem" KEY_FILE: "/etc/crowdsec/certs/server-key.pem"
CACERT_FILE: "/etc/crowdsec/certs/inter.pem" CACERT_FILE: "/etc/crowdsec/certs/inter.pem"
# CLIENT_CERT_FILE: "/etc/crowdsec/certs/bouncer.pem"
# CLIENT_CERT_FILE: "/etc/crowdsec/certs/bouncer.pem"
AGENTS_ALLOWED_OU: "agent-ou" AGENTS_ALLOWED_OU: "agent-ou"
BOUNCERS_ALLOWED_OU: "bouncer-ou" BOUNCERS_ALLOWED_OU: "bouncer-ou"
LEVEL_DEBUG: "true" LEVEL_DEBUG: "true"
# Disabled because it restart in loop otherwise # Disabled because it restart in loop otherwise
DISABLE_AGENT: "true" # DISABLE_AGENT: "true"
# Disabled for the examples # Disabled for the examples
DISABLE_ONLINE_API: "true" DISABLE_ONLINE_API: "true"
volumes: volumes:
@@ -97,6 +98,8 @@ services:
- crowdsec-config-tls-auth:/etc/crowdsec/ - crowdsec-config-tls-auth:/etc/crowdsec/
labels: labels:
- "traefik.enable=false" - "traefik.enable=false"
depends_on:
- gencert
gencert: gencert:
build: . build: .
@@ -1,8 +1,6 @@
version: "3.8"
services: services:
traefik: traefik:
image: "traefik:v2.10.4" image: "traefik:v3.0.0"
container_name: "traefik" container_name: "traefik"
restart: unless-stopped restart: unless-stopped
command: command:
@@ -15,7 +13,7 @@ services:
- "--entrypoints.web.address=:80" - "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.1.13" - "--experimental.plugins.bouncer.version=v1.3.0"
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" # - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
@@ -29,7 +27,7 @@ services:
whoami1: whoami1:
image: traefik/whoami image: traefik/whoami
container_name: "simple-service-foo" container_name: "simple-service-foo-trustedips"
restart: unless-stopped restart: unless-stopped
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
@@ -48,7 +46,7 @@ services:
whoami2: whoami2:
image: traefik/whoami image: traefik/whoami
container_name: "simple-service-bar" container_name: "simple-service-bar-trustedips"
restart: unless-stopped restart: unless-stopped
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
@@ -67,7 +65,7 @@ services:
crowdsec: crowdsec:
image: crowdsecurity/crowdsec:v1.5.2 image: crowdsecurity/crowdsec:v1.6.1-2
container_name: "crowdsec" container_name: "crowdsec"
restart: unless-stopped restart: unless-stopped
environment: environment:
+2
View File
@@ -86,6 +86,8 @@ func (c *Client) ServeHTTP(rw http.ResponseWriter, r *http.Request, remoteIP str
http.Redirect(rw, r, r.URL.String(), http.StatusFound) http.Redirect(rw, r, r.URL.String(), http.StatusFound)
return return
} }
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
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,
"FrontendJS": captcha[c.provider].js, "FrontendJS": captcha[c.provider].js,
+6
View File
@@ -57,6 +57,7 @@ type Config struct {
CrowdsecCapiPasswordFile string `json:"crowdsecCapiPasswordFile,omitempty"` CrowdsecCapiPasswordFile string `json:"crowdsecCapiPasswordFile,omitempty"`
CrowdsecCapiScenarios []string `json:"crowdsecCapiScenarios,omitempty"` CrowdsecCapiScenarios []string `json:"crowdsecCapiScenarios,omitempty"`
UpdateIntervalSeconds int64 `json:"updateIntervalSeconds,omitempty"` UpdateIntervalSeconds int64 `json:"updateIntervalSeconds,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"`
ForwardedHeadersCustomName string `json:"forwardedHeadersCustomName,omitempty"` ForwardedHeadersCustomName string `json:"forwardedHeadersCustomName,omitempty"`
@@ -100,6 +101,7 @@ func New() *Config {
CrowdsecLapiKey: "", CrowdsecLapiKey: "",
CrowdsecLapiTLSInsecureVerify: false, CrowdsecLapiTLSInsecureVerify: false,
UpdateIntervalSeconds: 60, UpdateIntervalSeconds: 60,
UpdateMaxFailure: 0,
DefaultDecisionSeconds: 60, DefaultDecisionSeconds: 60,
HTTPTimeoutSeconds: 10, HTTPTimeoutSeconds: 10,
CaptchaProvider: "", CaptchaProvider: "",
@@ -318,6 +320,10 @@ func validateParamsRequired(config *Config) error {
return fmt.Errorf("%v: cannot be less than 1", key) return fmt.Errorf("%v: cannot be less than 1", key)
} }
} }
if config.UpdateMaxFailure < -1 {
return fmt.Errorf("UpdateMaxFailure: cannot be less than -1")
}
if !contains([]string{NoneMode, LiveMode, StreamMode, AloneMode, AppsecMode}, config.CrowdsecMode) { if !contains([]string{NoneMode, LiveMode, StreamMode, AloneMode, AppsecMode}, config.CrowdsecMode) {
return fmt.Errorf("CrowdsecMode: must be one of 'none', 'live', 'stream', 'alone' or 'appsec'") return fmt.Errorf("CrowdsecMode: must be one of 'none', 'live', 'stream', 'alone' or 'appsec'")
} }