diff --git a/Makefile b/Makefile index efb1010..1906caf 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,9 @@ run_behindproxy: run_cacheredis: docker-compose -f exemples/redis-cache/docker-compose.redis.yml up -d --remove-orphans +run_trustedips: + docker-compose -f exemples/trusted-ips/docker-compose.redis.yml up -d --remove-orphans + run: docker-compose -f docker-compose.yml up -d --remove-orphans @@ -55,6 +58,7 @@ show_dev_logs: clean_all_docker: docker-compose -f exemples/behind-proxy/docker-compose.cloudflare.yml down --remove-orphans docker-compose -f exemples/redis-cache/docker-compose.redis.yml down --remove-orphans + docker-compose -f exemples/trusted-ips/docker-compose.redis.yml down --remove-orphans docker-compose -f docker-compose.local.yml down --remove-orphans docker-compose -f docker-compose.yml down --remove-orphans diff --git a/README.md b/README.md index d8c79e8..4974327 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,8 @@ http: forwardedHeadersTrustedIPs: - 10.0.10.23/32 - 10.0.20.0/24 + trustedIPs: + - 192.168.1.0/24 forwardedHeadersCustomName: X-Custom-Header redisCacheEnabled: false redisCacheHost: "redis:6379" @@ -181,8 +183,8 @@ docker-compose up -d ```bash docker-compose up -d crowdsec -docker exec crowdsec cscli decisions add --ip 10.0.0.10 # this will be effective 4h -docker exec crowdsec cscli decisions remove --ip 10.0.0.10 +docker exec crowdsec cscli decisions add --ip 10.0.0.10 -d 10m # this will be effective 10min +docker exec crowdsec cscli decisions remove --ip 10.0.0.10 -d 10m ``` ### Local Mode @@ -236,7 +238,7 @@ We configure the middleware to trust as well the IP: - "traefik.http.middlewares.crowdsec1.plugin.bouncer.forwardedheaderstrustedips=172.21.0.5" ``` -To run the environnement run: +To play the demo environnement run: ```bash make run_behindproxy ``` @@ -249,11 +251,49 @@ The plugin must be configured to connect to a redis instance ``` Here **redis** is the hostname of a container located in the same network as Traefik and **6379** the default port of redis -To run the demo environnement run: +To play the demo environnement run: ```bash make run_cacheredis ``` +3. Using Trusted IP (ex: LAN OR VPN) that won't get filtered by crowdsec + +You need to configure your Traefik to trust Forwarded headers by your front proxy +FIXME +// In the exemple we use another instance of traefik with the container to simulate a front proxy + +The "internal" Traefik instance is configured to trust the forward headers +```yaml + - "--entrypoints.web.forwardedheaders.trustedips=172.21.0.5" +``` + +We configure the middleware to trust as well the IP: +```yaml + - "traefik.http.middlewares.crowdsec.plugin.bouncer.forwardedheaderstrustedips=172.21.0.5" +``` + +Add your IP to the ban list +```bash +docker exec crowdsec cscli decisions add --ip 10.0.10.30 -d 10m +``` +You should get a 403 on http://localhost/foo + +> Replace *10.0.10.30* by your IP + +And the IPS that will not be filtered by the plugin +```yaml + - "traefik.http.middlewares.crowdsec.plugin.bouncer.trustedips=10.0.10.30/32" +``` + +> Replace *10.0.10.30/32* by your IP or IP range, so it's not getting checked against ban cache of crowdsec + +You should get a 200 on http://localhost/foo + +To play the demo environnement run: +```bash +make run_trustedips +``` + ### About Me and [mathieuHa](https://github.com/mathieuHa) have been using traefik since 2020 at [Primadviz](https://primadviz.com). diff --git a/bouncer.go b/bouncer.go index ae13c31..2c5df3e 100644 --- a/bouncer.go +++ b/bouncer.go @@ -53,7 +53,7 @@ type Config struct { func CreateConfig() *Config { return &Config{ Enabled: false, - LogLevel: "DEBUG", + LogLevel: "INFO", CrowdsecMode: liveMode, CrowdsecLapiScheme: "http", CrowdsecLapiHost: "crowdsec:8080", @@ -162,7 +162,6 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { bouncer.next.ServeHTTP(rw, req) return } - logger.Debug(fmt.Sprintf("ServeHTTP ip:%v", remoteHost)) if bouncer.CheckerTrusted == nil { logger.Debug("CheckerTrusted == nil") } else { @@ -173,7 +172,9 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } // if our IP is in the trusted list we bypass the next checks if trusted { + logger.Debug(fmt.Sprintf("IP %v is trusted", remoteHost)) bouncer.next.ServeHTTP(rw, req) + return } } diff --git a/docker-compose.yml b/docker-compose.yml index b8cf741..13ae2f5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,7 +13,7 @@ services: - "--entrypoints.web.address=:80" - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" - - "--experimental.plugins.bouncer.version=v1.1.0" + - "--experimental.plugins.bouncer.version=v1.1.2" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" - "logs:/var/log/traefik" diff --git a/exemples/behind-proxy/docker-compose.cloudflare.yml b/exemples/behind-proxy/docker-compose.cloudflare.yml index 0e46395..03c81c0 100644 --- a/exemples/behind-proxy/docker-compose.cloudflare.yml +++ b/exemples/behind-proxy/docker-compose.cloudflare.yml @@ -34,7 +34,7 @@ services: - "--entrypoints.web.forwardedheaders.trustedips=172.21.0.5" - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" - - "--experimental.plugins.bouncer.version=v1.1.0" + - "--experimental.plugins.bouncer.version=v1.2.0" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - logs-dev:/var/log/traefik diff --git a/exemples/redis-cache/docker-compose.redis.yml b/exemples/redis-cache/docker-compose.redis.yml index 318f00e..9eb70c6 100644 --- a/exemples/redis-cache/docker-compose.redis.yml +++ b/exemples/redis-cache/docker-compose.redis.yml @@ -33,7 +33,7 @@ services: labels: - "traefik.enable=true" # Definition of the router - - "traefik.http.routers.router1.rule=Host(`localhost`)" + - "traefik.http.routers.router1.rule=Path(`/foo`)" - "traefik.http.routers.router1.entrypoints=web" - "traefik.http.routers.router1.middlewares=crowdsec1@docker" # Definition of the service diff --git a/exemples/trusted-ips/acquis.yaml b/exemples/trusted-ips/acquis.yaml new file mode 100644 index 0000000..5d52554 --- /dev/null +++ b/exemples/trusted-ips/acquis.yaml @@ -0,0 +1,4 @@ +filenames: + - /var/log/traefik/access.log +labels: + type: traefik diff --git a/exemples/trusted-ips/docker-compose.redis.yml b/exemples/trusted-ips/docker-compose.redis.yml new file mode 100644 index 0000000..ffc99ed --- /dev/null +++ b/exemples/trusted-ips/docker-compose.redis.yml @@ -0,0 +1,66 @@ +version: "3.8" + +services: + traefik: + image: "traefik:v2.9.4" + container_name: "traefik" + command: + # - "--log.level=DEBUG" + - "--accesslog" + - "--accesslog.filepath=/var/log/traefik/access.log" + - "--api.insecure=true" + - "--providers.docker=true" + - "--providers.docker.exposedbydefault=false" + - "--entrypoints.web.address=:80" + + - "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" + - "--experimental.plugins.bouncer.version=v1.1.2" + # - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - logs-trustedips:/var/log/traefik + - ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin + ports: + - 80:80 + - 8080:8080 + depends_on: + - crowdsec + + whoami1: + image: traefik/whoami + container_name: "simple-service1" + labels: + - "traefik.enable=true" + # Definition of the router + - "traefik.http.routers.router1.rule=Path(`/foo`)" + - "traefik.http.routers.router1.entrypoints=web" + - "traefik.http.routers.router1.middlewares=crowdsec@docker" + # Definition of the service + - "traefik.http.services.service1.loadbalancer.server.port=80" + # Definition of the middleware + - "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true" + # crowdseclapikey must be uniq to the middleware attached to the service + - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5" + # Replace 10.0.10.30/32 by your IP range which is "trusted" + - "traefik.http.middlewares.crowdsec.plugin.bouncer.trustedips=10.0.10.30/32" + + + crowdsec: + image: crowdsecurity/crowdsec:v1.4.1 + container_name: "crowdsec" + environment: + COLLECTIONS: crowdsecurity/traefik + CUSTOM_HOSTNAME: crowdsec + BOUNCER_KEY_TRAEFIK_DEV_1: 40796d93c2958f9e58345514e67740e5 + volumes: + - ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro + - logs-trustedips:/var/log/traefik:ro + - crowdsec-db-trustedips:/var/lib/crowdsec/data/ + - crowdsec-config-trustedips:/etc/crowdsec/ + labels: + - "traefik.enable=false" + +volumes: + logs-trustedips: + crowdsec-db-trustedips: + crowdsec-config-trustedips: diff --git a/pkg/ip/ip.go b/pkg/ip/ip.go index 622d8ba..69914c7 100644 --- a/pkg/ip/ip.go +++ b/pkg/ip/ip.go @@ -6,6 +6,8 @@ import ( "net" "net/http" "strings" + + "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/pkg/logger" ) // CHECKER @@ -27,6 +29,7 @@ func NewChecker(trustedIPs []string) (*Checker, error) { for _, ipMask := range trustedIPs { if ipAddr := net.ParseIP(ipMask); ipAddr != nil { checker.authorizedIPs = append(checker.authorizedIPs, &ipAddr) + logger.Debug(fmt.Sprintf("IP %v is trusted", ipAddr)) continue } @@ -35,6 +38,7 @@ func NewChecker(trustedIPs []string) (*Checker, error) { return nil, fmt.Errorf("parsing CIDR trusted IPs %s: %w", ipAddr, err) } checker.authorizedIPsNet = append(checker.authorizedIPsNet, ipAddr) + logger.Debug(fmt.Sprintf("IP network %v is trusted", ipAddr)) } return checker, nil