Skip to content

Commit

Permalink
[DOC] Completing README.md, changing env var, adding "forbidden" if b…
Browse files Browse the repository at this point in the history
…ounce
  • Loading branch information
fbonalair committed Oct 2, 2021
1 parent 3043f41 commit 2c4d120
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
CROWDSEC_BOUNCER_API_KEY=40796d93c2958f9e58345514e67740e5
CROWDSEC_BOUNCER_HOST=localhost:8083
CROWDSEC_AGENT_HOST=localhost:8083
2 changes: 1 addition & 1 deletion .idea/runConfigurations/run_server.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 59 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,62 @@
# traefik-crowdsec-bouncer
A http service to verify request according to decisions made by CrowdSec.
A http service to verify request and bounce them according to decisions made by CrowdSec.

# Contribution
# 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.

# 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:
```
git clone https://github.com/fbonalair/traefik-crowdsec-bouncer.git
cd examples
docker-compose up -d traefik crowdsec whoami
```

## Procedure
1. Get an 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. Past this 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. Start a browser and visit `http://localhost/`. 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 `http://localhost/` again, in your browser you will see "Forbidden" since this time since you've been banned.
Though 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 `http://localhost/` 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.

## 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)

## Installation
# TODO
- go get
- docker-compose up
- docker-compose exec crowdsec cscli bouncers add MyTestClient
- save API KEY
- Add key to server env var CROWDSEC_BOUNCER_API_KEY
## 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`
* `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

## Exposed routes
The webservice exposes 3 routes:

* `/api/v1/forwardAuth` - Main route to be used by Traefik: query CrowdSec agent with the header `X-Real-Ip` as client IP`
* `/api/v1/ping` - Simple health route that respond pong with http 200`
* `/api/v1/healthz` - Another health route that query CrowdSec agent with localhost (127.0.0.1)`

# Contribution
TBD
2 changes: 1 addition & 1 deletion _test.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
GIN_MODE=release

CROWDSEC_BOUNCER_API_KEY=40796d93c2958f9e58345514e67740e5
CROWDSEC_BOUNCER_HOST=localhost:8083
CROWDSEC_AGENT_HOST=localhost:8083
3 changes: 2 additions & 1 deletion bouncer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ func TestForwardAuthInvalidIp(t *testing.T) {
req, _ := http.NewRequest("GET", "/api/v1/forwardAuth", nil)
router.ServeHTTP(w, req)

assert.Equal(t, 401, w.Code)
assert.Equal(t, 403, w.Code)
assert.Equal(t, "Forbidden", w.Body.String())
}

func TestForwardAuthValidIp(t *testing.T) {
Expand Down
6 changes: 3 additions & 3 deletions controler/controler.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const (
)

var crowdsecBouncerApiKey = RequiredEnv("CROWDSEC_BOUNCER_API_KEY")
var crowdsecBouncerHost = RequiredEnv("CROWDSEC_BOUNCER_HOST")
var crowdsecBouncerHost = RequiredEnv("CROWDSEC_AGENT_HOST")
var crowdsecBouncerScheme = OptionalEnv("CROWDSEC_BOUNCER_SCHEME", "http")

var client = &http.Client{
Expand Down Expand Up @@ -83,7 +83,7 @@ func callSecApi(c *gin.Context, realIP string) {

// Authorization logic
if len(decisions) > 0 {
c.Status(http.StatusUnauthorized)
c.String(http.StatusForbidden, "Forbidden")
} else {
c.Status(http.StatusOK)
}
Expand Down Expand Up @@ -111,5 +111,5 @@ func Ping(c *gin.Context) {

func remedyError(err error, c *gin.Context) {
_ = c.Error(err) // nil err should be handled earlier
c.Status(http.StatusUnauthorized)
c.String(http.StatusForbidden, "Forbidden")
}
20 changes: 2 additions & 18 deletions docker-compose-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,31 +46,15 @@ services:

api:
build: .
image: traefik-crowdsec-bouncer
image: fbonalair/traefik-crowdsec-bouncer
container_name: bouncer
environment:
PORT: 8082
CROWDSEC_BOUNCER_API_KEY: 40796d93c2958f9e58345514e67740e5
CROWDSEC_BOUNCER_HOST: crowdsec:8083
CROWDSEC_AGENT_HOST: crowdsec:8083
ports:
- "8082:8080"

# dashboard:
# build: ./crowdsec/dashboard
# ports:
# - 3000:3000
# environment:
# MB_DB_FILE: /data/metabase.db
# MGID: "${GID-1000}"
# depends_on:
# - 'crowdsec'
# volumes:
# - crowdsec-db:/metabase-data/
## networks:
## crowdsec_test:
## ipv4_address: 172.20.0.5
# # credentials: [email protected] and !!Cr0wdS3c_M3t4b4s3??

volumes:
logs:
crowdsec-db:
Expand Down
15 changes: 4 additions & 11 deletions examples/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: "3.8"

services:
traefik:
image: "traefik:v2.5"
image: traefik:v2.5
container_name: "traefik-example"
command:
# - "--log.level=DEBUG"
Expand All @@ -12,12 +12,11 @@ services:
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"

whoami:
image: traefik/whoami
image: traefik/whoami:latest
container_name: "simple-service-example"
labels:
# Create bouncer middleware
Expand All @@ -38,21 +37,15 @@ services:
depends_on:
- 'traefik'
volumes:
# - ./crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml
# - logs-example:/var/log/nginx
- crowdsec-db-example:/var/lib/crowdsec/data/
- crowdsec-config-example:/etc/crowdsec/
ports:
- 8083:8080

bouncer:
image: fbonalair/bouncer
image: fbonalair/traefik-crowdsec-bouncer:0.1.1
container_name: "bouncer-example"
environment:
CROWDSEC_BOUNCER_API_KEY: MyApiKey
CROWDSEC_BOUNCER_HOST: crowdsec:8080
ports:
- "8082:8080"
CROWDSEC_AGENT_HOST: crowdsec:8080

volumes:
logs-example:
Expand Down

0 comments on commit 2c4d120

Please sign in to comment.