diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index f27e3cb..41d9cf1 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -23,7 +23,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- language: [ 'go', 'javascript' ]
+ language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
diff --git a/.gitleaks.toml b/.gitleaks.toml
new file mode 100644
index 0000000..6c055bc
--- /dev/null
+++ b/.gitleaks.toml
@@ -0,0 +1,19 @@
+# Title for the gitleaks configuration file.
+title = "Gitleaks configuration"
+
+# Extend the base (this) configuration. When you extend a configuration
+# the base rules take precendence over the extended rules. I.e, if there are
+# duplicate rules in both the base configuration and the extended configuration
+# the base rules will override the extended rules.
+# Another thing to know with extending configurations is you can chain together
+# multiple configuration files to a depth of 2. Allowlist arrays are appended
+# and can contain duplicates.
+# useDefault and path can NOT be used at the same time. Choose one.
+[extend]
+# useDefault will extend the base configuration with the default gitleaks config:
+# https://github.com/zricethezav/gitleaks/blob/master/config/gitleaks.toml
+useDefault = true
+
+[allowlist]
+description = "ignore commit 04a37c7014c951bdf6bc3c8bbc802c9a772bf96a"
+commits = [ "04a37c7014c951bdf6bc3c8bbc802c9a772bf96a", "3effed4e2c73249b9cb35718efdadbda9614a6b3"]
diff --git a/README.md b/README.md
index 90ca91d..9f1fc65 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+
![GitHub](https://img.shields.io/github/license/fbonalair/traefik-crowdsec-bouncer)
![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/fbonalair/traefik-crowdsec-bouncer)
[![Go Report Card](https://goreportcard.com/badge/github.com/fbonalair/traefik-crowdsec-bouncer)](https://goreportcard.com/report/github.com/fbonalair/traefik-crowdsec-bouncer)
@@ -7,19 +8,21 @@
![Docker Image Size (latest semver)](https://img.shields.io/docker/image-size/fbonalair/traefik-crowdsec-bouncer)
# traefik-crowdsec-bouncer
-A http service to verify request and bounce them according to decisions made by CrowdSec.
+
+A HTTP service to verify requests and bounce them according to decisions made by CrowdSec.
# Description
-This repository aim to implement a [CrowdSec](https://doc.crowdsec.net/) bouncer for the router [Traefik](https://doc.traefik.io/traefik/) to block malicious IP to access your services.
-For this it leverages [Traefik v2 ForwardAuth middleware](https://doc.traefik.io/traefik/middlewares/http/forwardauth/) and query CrowdSec with client IP.
-If the client IP is on ban list, it will get a http code 403 response. Otherwise, request will continue as usual.
+
+This repository aims to implement a [CrowdSec](https://doc.crowdsec.net/) bouncer for the router [Traefik](https://doc.traefik.io/traefik/) to block malicious IPs from accessing your services. It leverages the [Traefik v2 ForwardAuth middleware](https://doc.traefik.io/traefik/middlewares/http/forwardauth/) and queries CrowdSec with the client IP. If the client IP is on the ban list, it will receive a HTTP code 403 response. Otherwise, the request will continue as usual.
# Demo
+
## Prerequisites
-[Docker](https://docs.docker.com/get-docker/) and [Docker-compose](https://docs.docker.com/compose/install/) installed.
-You can use the docker-compose in the examples' folder as a starting point.
-Through traefik it exposes the whoami countainer on port 80, with the bouncer accepting and rejecting client IP.
-Launch your all services except the bouncer with the follow commands:
+
+Ensure [Docker](https://docs.docker.com/get-docker/) and [Docker-compose](https://docs.docker.com/compose/install/) are installed. You can use the docker-compose file in the examples folder as a starting point. Through Traefik, it exposes the whoami container on port 80, with the bouncer accepting and rejecting client IPs.
+
+Launch all services except the bouncer with the following commands:
+
```bash
git clone https://github.com/fbonalair/traefik-crowdsec-bouncer.git && \
cd traefik-crowdsec-bouncer/examples && \
@@ -27,59 +30,62 @@ git clone https://github.com/fbonalair/traefik-crowdsec-bouncer.git && \
```
## Procedure
-1. Get a bouncer API key from CrowdSec with command `docker exec crowdsec-example cscli bouncers add traefik-bouncer`
-2. Copy the API key printed. You **_WON'T_** be able the get it again.
-3. Paste this API key as the value for bouncer environment variable `CROWDSEC_BOUNCER_API_KEY`, instead of "MyApiKey"
-4. Start bouncer in attach mode with `docker-compose up bouncer`
-5. Visit . You will see the container whoami page, copy your IP address from `X-Real-Ip` line (i.e. 192.168.128.1).
-In your console, you will see lines showing your authorized request (i.e. "status":200).
-6. In another console, ban your IP with command `docker exec crowdsec-example cscli decisions add --ip 192.168.128.1`, modify the IP with your address.
-7. Visit again, in your browser you will see "Forbidden" since this time since you've been banned.
-Though the console you will see "status":403.
+
+1. Get a bouncer API key from CrowdSec with the command `docker exec crowdsec-example cscli bouncers add traefik-bouncer`
+2. Copy the printed API key. You **_WON'T_** be able to retrieve it again.
+3. Paste this API key as the value for the bouncer environment variable `CROWDSEC_BOUNCER_API_KEY`, instead of "MyApiKey"
+4. Start the bouncer in attach mode with `docker-compose up bouncer`
+5. Visit . You will see the container whoami page. Copy your IP address from the `X-Real-Ip` line (e.g., 192.168.128.1).
+ In your console, you will see lines showing your authorized request (i.e., "status": 200).
+6. In another console, ban your IP with the command `docker exec crowdsec-example cscli decisions add --ip 192.168.128.1`, replacing the IP with your address.
+7. Visit again. In your browser, you will see "Forbidden" since you have been banned.
+ In the console, you will see "status": 403.
8. Unban yourself with `docker exec crowdsec-example cscli decisions delete --ip 192.168.128.1`
-9. Visit one last time, you will have access to the container whoami.
+9. Visit one last time. You will have access to the container whoami.
Enjoy!
# Usage
-For now, this web service is mainly fought to be used as a container.
-If you need to build from source, you can get some inspiration from the Dockerfile.
+
+For now, this web service is mainly intended to be used as a container. If you need to build from source, you can get some inspiration from the Dockerfile.
## Prerequisites
-You should have Traefik v2 and a CrowdSec instance running.
-The container is available on docker as image `fbonalair/traefik-crowdsec-bouncer`. Host it as you see fit, though it must have access to CrowdSec and be accessible by Traefik.
-Follow [traefik v2 ForwardAuth middleware](https://doc.traefik.io/traefik/middlewares/http/forwardauth/) documentation to create a forwardAuth middle pointing to your bouncer host.
-Generate a bouncer API key following [CrowdSec documentation](https://doc.crowdsec.net/docs/cscli/cscli_bouncers_add)
+
+You should have Traefik v2 and a CrowdSec instance running. The container is available on Docker as the image `fbonalair/traefik-crowdsec-bouncer`. Host it as you see fit, though it must have access to CrowdSec and be accessible by Traefik. Follow the [Traefik v2 ForwardAuth middleware](https://doc.traefik.io/traefik/middlewares/http/forwardauth/) documentation to create a forwardAuth middle pointing to your bouncer host. Generate a bouncer API key following [CrowdSec documentation](https://doc.crowdsec.net/docs/cscli/cscli_bouncers_add).
## Configuration
-The webservice configuration is made via environment variables:
-
-* `CROWDSEC_BOUNCER_API_KEY` - CrowdSec bouncer API key required to be authorized to request local API (required)`
-* `CROWDSEC_AGENT_HOST` - Host and port of CrowdSec agent, i.e. crowdsec-agent:8080 (required)`
-* `CROWDSEC_BOUNCER_SCHEME` - Scheme to query CrowdSec agent. Expected value: http, https. Default to http`
-* `CROWDSEC_BOUNCER_LOG_LEVEL` - Minimum log level for bouncer. Expected value [zerolog levels](https://pkg.go.dev/github.com/rs/zerolog#readme-leveled-logging). Default to 1
-* `CROWDSEC_BOUNCER_BAN_RESPONSE_CODE` - HTTP code to respond in case of ban. Default to 403
-* `CROWDSEC_BOUNCER_BAN_RESPONSE_MSG` - HTTP body as message to respond in case of ban. Default to Forbidden
-* `HEALTH_CHECKER_TIMEOUT_DURATION` - [Golang string represation of a duration](https://pkg.go.dev/time#ParseDuration) to wait for bouncer's answer before failing health check. Default to 2s
-* `PORT` - Change listening port of web server. Default listen on 8080
-* `GIN_MODE` - By default, run app in "debug" mode. Set it to "release" in production
-* `TRUSTED_PROXIES` - List of trusted proxies IP addresses in CIDR format, delimited by ','. Default of 0.0.0.0/0 should be fine for most use cases, but you HAVE to add them directly in Traefik.
-
-## Exposed routes
-The webservice exposes some routes:
-
-* GET `/api/v1/forwardAuth` - Main route to be used by Traefik: query CrowdSec agent with the header `X-Real-Ip` as client IP`
-* GET `/api/v1/ping` - Simple health route that respond pong with http 200`
-* GET `/api/v1/healthz` - Another health route that query CrowdSec agent with localhost (127.0.0.1)`
-* GET `/api/v1/metrics` - Prometheus route to scrap metrics
+
+The web service configuration is managed via environment variables:
+
+* `CROWDSEC_BOUNCER_API_KEY` - CrowdSec bouncer API key required to authorize requests to the local API (required)
+* `CROWDSEC_AGENT_HOST` - Host and port of the CrowdSec agent, e.g., crowdsec-agent:8080 (required)
+* `CROWDSEC_BOUNCER_SCHEME` - Scheme to query the CrowdSec agent. Expected values: http, https. Defaults to http
+* `CROWDSEC_BOUNCER_LOG_LEVEL` - Minimum log level for the bouncer. Expected values: [zerolog levels](https://pkg.go.dev/github.com/rs/zerolog#readme-leveled-logging). Defaults to 1
+* `CROWDSEC_BOUNCER_BAN_RESPONSE_CODE` - HTTP code to respond in case of a ban. Defaults to 403
+* `CROWDSEC_BOUNCER_BAN_RESPONSE_MSG` - HTTP body message to respond in case of a ban. Defaults to "Forbidden"
+* `HEALTH_CHECKER_TIMEOUT_DURATION` - [Golang string representation of a duration](https://pkg.go.dev/time#ParseDuration) to wait for the bouncer's answer before failing the health check. Defaults to 2s
+* `PORT` - Change the listening port of the web server. Defaults to 8080
+* `GIN_MODE` - By default, runs the app in "debug" mode. Set it to "release" in production
+* `TRUSTED_PROXIES` - List of trusted proxies' IP addresses in CIDR format, delimited by commas. Default is 0.0.0.0/0, which should be fine for most use cases, but you MUST add them directly in Traefik.
+
+## Exposed Routes
+
+The web service exposes the following routes:
+
+* GET `/api/v1/forwardAuth` - Main route to be used by Traefik: queries the CrowdSec agent with the header `X-Real-Ip` as the client IP
+* GET `/api/v1/ping` - Simple health route that responds with "pong" and HTTP 200
+* GET `/api/v1/healthz` - Another health route that queries the CrowdSec agent with localhost (127.0.0.1)
+* GET `/api/v1/metrics` - Prometheus route to scrape metrics
# Contribution
-Any constructive feedback is welcome, fill free to add an issue or a pull request. I will review it and integrate it to the code.
+
+Any constructive feedback is welcome. Feel free to add an issue or a pull request. I will review it and integrate it into the code.
## Local Setup
-1. Start docker compose with docker-compose up -d
-2. Create `_test.env` from template `_test.env.example` such as `cp _test.env.example _test.env`
-3. Get an API key for your bouncer with : ` docker exec traefik-crowdsec-bouncer-crowdsec-1 cscli bouncers add traefik-bouncer`
-4. In `_test.env` replace `` with the previously generated key
-5. Adding a banned IP to your crodwsec instance with : `docker exec traefik-crowdsec-bouncer-crowdsec-1 cscli decisions add -i 1.2.3.4`
-6. Run test with `godotenv -f ./_test.env go test -cover`
+
+1. Start docker-compose with `docker-compose up -d`
+2. Create `_test.env` from the template `_test.env.example` with the command `cp _test.env.example _test.env`
+3. Get an API key for your bouncer with the command `docker exec traefik-crowdsec-bouncer-crowdsec-1 cscli bouncers add traefik-bouncer`
+4. In `_test.env`, replace `` with the previously generated key
+5. Add a banned IP to your CrowdSec instance with the command `docker exec traefik-crowdsec-bouncer-crowdsec-1 cscli decisions add -i 1.2.3.4`
+6. Run tests with `godotenv -f ./_test.env go test -cover`
diff --git a/bouncer.go b/bouncer.go
index 134ebb3..ddb3fb8 100644
--- a/bouncer.go
+++ b/bouncer.go
@@ -4,7 +4,7 @@ import (
"os"
. "github.com/fbonalair/traefik-crowdsec-bouncer/config"
- "github.com/fbonalair/traefik-crowdsec-bouncer/controler"
+ "github.com/fbonalair/traefik-crowdsec-bouncer/controller"
"github.com/gin-contrib/logger"
"github.com/gin-gonic/gin"
"github.com/rs/zerolog"
@@ -58,9 +58,9 @@ func setupRouter() (*gin.Engine, error) {
router.Use(logger.SetLogger(
logger.WithSkipPath([]string{"/api/v1/ping", "/api/v1/healthz"}),
))
- router.GET("/api/v1/ping", controler.Ping)
- router.GET("/api/v1/healthz", controler.Healthz)
- router.GET("/api/v1/forwardAuth", controler.ForwardAuth)
- router.GET("/api/v1/metrics", controler.Metrics)
+ router.GET("/api/v1/ping", controller.Ping)
+ router.GET("/api/v1/healthz", controller.Healthz)
+ router.GET("/api/v1/forwardAuth", controller.ForwardAuth)
+ router.GET("/api/v1/metrics", controller.Metrics)
return router, nil
}
diff --git a/controler/controler.go b/controller/controller.go
similarity index 86%
rename from controler/controler.go
rename to controller/controller.go
index 2f6aae0..200a02f 100644
--- a/controler/controler.go
+++ b/controller/controller.go
@@ -1,4 +1,4 @@
-package controler
+package controller
import (
"bytes"
@@ -8,7 +8,6 @@ import (
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
"io"
- "io/ioutil"
"net/http"
"net/url"
"strconv"
@@ -52,7 +51,7 @@ var client = &http.Client{
Call Crowdsec local IP and with realIP and return true if IP does NOT have a ban decisions.
*/
func isIpAuthorized(clientIP string) (bool, error) {
- // Generating crowdsec API request
+ // Generate Crowdsec API request
decisionUrl := url.URL{
Scheme: crowdsecBouncerScheme,
Host: crowdsecBouncerHost,
@@ -67,42 +66,38 @@ func isIpAuthorized(clientIP string) (bool, error) {
log.Debug().
Str("method", http.MethodGet).
Str("url", decisionUrl.String()).
- Msg("Request Crowdsec's decision Local API")
+ Msg("Requesting Crowdsec's decision Local API")
- // Calling crowdsec API
+ // Call Crowdsec API
resp, err := client.Do(req)
if err != nil {
return false, err
}
+ defer resp.Body.Close()
+
if resp.StatusCode == http.StatusForbidden {
- return false, err
+ return false, nil
}
- // Parsing response
- defer func(Body io.ReadCloser) {
- err := Body.Close()
- if err != nil {
- log.Err(err).Msg("An error occurred while closing body reader")
- }
- }(resp.Body)
- reqBody, err := ioutil.ReadAll(resp.Body)
+ // Parse response
+ respBody, err := io.ReadAll(resp.Body)
if err != nil {
return false, err
}
- if bytes.Equal(reqBody, []byte("null")) {
+ if bytes.Equal(respBody, []byte("null")) {
log.Debug().Msgf("No decision for IP %q. Accepting", clientIP)
return true, nil
}
- log.Debug().RawJSON("decisions", reqBody).Msg("Found Crowdsec's decision(s), evaluating ...")
+ log.Debug().RawJSON("decisions", respBody).Msg("Found Crowdsec's decision(s), evaluating ...")
var decisions []model.Decision
- err = json.Unmarshal(reqBody, &decisions)
+ err = json.Unmarshal(respBody, &decisions)
if err != nil {
return false, err
}
// Authorization logic
- return len(decisions) < 0, nil
+ return len(decisions) == 0, nil
}
/*
diff --git a/go.mod b/go.mod
index a456dc7..e094d83 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,8 @@
module github.com/fbonalair/traefik-crowdsec-bouncer
-go 1.22
+go 1.21
+
+toolchain go1.22.2
require (
github.com/gin-contrib/logger v1.1.2
@@ -52,4 +54,4 @@ replace github.com/fbonalair/traefik-crowdsec-bouncer/config => ../config
replace github.com/fbonalair/traefik-crowdsec-bouncer/model => ../model
-replace github.com/fbonalair/traefik-crowdsec-bouncer/controler => ../controler
+replace github.com/fbonalair/traefik-crowdsec-bouncer/controller => ../controller
diff --git a/healthcheck/go.mod b/healthcheck/go.mod
index 3882538..cf5b620 100644
--- a/healthcheck/go.mod
+++ b/healthcheck/go.mod
@@ -1,3 +1,3 @@
module github.com/fbonalair/healthcheck
-go 1.22
+go 1.21