Crowdsec Security for swag To Protect Your Nginx Reverse Proxy
6 min readNginx provided by LinuxServer.io. as swag, is a reverse proxy in a docker container. It offers a module to integrate Crowdsec! Swag crowdsec is the plugin, but Crowdsec bouncer also needs to be installed as a separate container. You can find various not-too-difficult guides just like mine, hopefully this one is even easier?
This guide is mainly for interfacing Crowdsec with swag nginx from LinuxServer.io, but can also be applied for local/manual installations of Nginx/Wordpress/etc. You will just have to integrate the nginx bouncer plugin yourself.
After a month or two, crowdsec with start hitting your CPU seriously. It’s not a bug, it’s by design. Switching from SQLite to MySQL won’t change a thing. Lots of regexp rules, and poorly optimized queries are the problem. It’s still worth it if you have the CPU to handle it, but I have to warn you.
Also the “Bad Bots” collection is the most demanding. Disabling it will drastically improve the situation.
Presentation
With Compose, we are going to setup a separate container for crowdsec. In a nutshell, nginx swag will call Crowdsec via an http API for every access to your websites. Crowdsec will in return decide if it’s a threat or not, and tell nginx to block the IP address or not. Calls can be done for each call or in stream mode every x seconds.
How does Crowdsec work?
This bouncer leverages nginx lua’s API, namely access_by_lua_block
to check e IP address against the local API.
Supported features:
- Live mode (query the local API for each request)
- Stream mode (pull the local API for new/old decisions every X seconds)
- Ban remediation (can ban an IP address by redirecting him or returning a custom HTML page)
- reCAPTCHA v2 remediation (can return a captcha)
- Works with IPv4/IPv6
- Support IP ranges (can apply a remediation on an IP range)
At the back, this bouncer uses crowdsec lua lib.
Quick How-To install Crowdsec Container with Docker
Step 1: Requisites and Prepare the File System
Personally, I use a central location for all the containers, let’s call it /app
mkdir -p /app/crowdsec/config mkdir -p /app/crowdsec/data alias cscli='docker exec -t crowdsec cscli'
You will need the cscli alias, believe me. Add cscli alias in your bashrc or profile.
You also need a clear separation of docker networks. Personally I use proxy, frontend and backend, plus other flavors. For this example you will need:
- Docker – I use community edition, version 20.10.25
- docker-compose – I use the latest version v2.22.0
- frontend docker network:
docker network add frontrend
- A working swag nginx instance with some proxified apps and sites
- Swag nginx and Crowdsec to be in the same docker network ๐
Step 2: Add docker-compose.yml under /app/crowdsec
version: "3.3" # alias cscli='docker exec -t crowdsec cscli' services: crowdsec: labels: com.centurylinklabs.watchtower.enable: "true" image: docker.io/crowdsecurity/crowdsec:latest container_name: crowdsec volumes: # I like to fix the timezone and time to the host - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro - /app/crowdsec/config:/etc/crowdsec - /app/crowdsec/data:/var/lib/crowdsec/data # below are all the log mounts needed for crowdsec to analyze - /app/swag/config/log/nginx:/var/log/swag:ro - /app/mailu/data/log/front:/var/log/mailu/front:ro - /var/log:/var/log/host:ro # do not expose anything, nginx will reach out from within its internal network # expose: # - "8080" # ports: # - "8080:8080" environment: TZ: ${TZ} GID: "${GID:-1000}" PUID: 1000 PGID: 1000 # LEVEL_DEBUG: true # You can add more collections for your containers later on COLLECTIONS: crowdsecurity/nginx crowdsecurity/http-cve crowdsecurity/whitelist-good-actors CUSTOM_HOSTNAME: ${HOSTNAME} # to enroll: cscli console enroll <your online crowdsec instance>; then restart container #CROWDSEC_INSTANCE: <your online crowdsec instance> #MAPQUEST_API_KEY: <generate your own for free> restart: unless-stopped security_opt: - no-new-privileges:true networks: frontend: networks: frontend: external: true name: frontend
Then start Crowdsec for the first time:
cd /app/crowdsec docker-compose pull docker-compose up -d --build docker-compose logs --tail=25 -f
Step 3: update swag bouncer and Crowdsec acquisitions
Generate the API key for your swag nginx instance: This is something you will have to do again for each client that will access this bouncer
cscli bouncers add swag # Api key for 'swag': # c8a3775a81b21c234422c1c0ad0a1234
This is the API key you will need to add in the compose file for swag nginx. Also add the crowdsec module in the list, along with the url to crowdsec:
environment: - DOCKER_MODS=linuxserver/mods:swag-auto-reload|..more modules..|linuxserver/mods:swag-crowdsec - CROWDSEC_API_KEY=c8a3775a81b21c234422c1c0ad0a1234 - CROWDSEC_LAPI_URL=http://crowdsec:8080
Next, edit the acquisitions file: this is where you define what log will be read in which format, adapt to your needs:
vi /app/crowdsec/config/acquis.yaml
filenames: - /var/log/swag/* - /var/log/mailu/front/* labels: type: nginx --- filenames: - /var/log/host/auth.log* - /var/log/host/syslog labels: type: syslog --- filenames: - /var/log/apache2/*.log labels: type: apache2
Step 4: restart swag and Crowdsec
cd /app/crowdsec docker-compose down && docker-compose up -d cd /app/swag docker-compose down && docker-compose up -d
You can verify that the Crowdsec module has been setup properly in nginx by accessing this file:
/app/swag/config/crowdsec/crowdsec-nginx-bouncer.conf
Step 5: monitor and enjoy
To see if swag nginx connected to Crowdsec: correct result if IP Address is not null amd you can see the client’s version
cscli bouncers list
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ Name IP Address Valid Last API pull Type Version Auth Type โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ swag 172.20.0.13 โ๏ธ 2022-12-24T18:49:50Z crowdsec-nginx-bouncer v1.0.4 api-key โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ ^--- check last refresh date, this proves the client is connected
Loot at the metrics and how many nefarious actors have been banned:
cscli metrics
Acquisition Metrics: โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโฎ โ Source โ Lines read โ Lines parsed โ Lines unparsed โ Lines poured to bucket โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโผโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโค โ file:/var/log/host/auth.log โ 4 โ - โ 4 โ - โ โ file:/var/log/host/syslog โ 1.83k โ - โ 1.83k โ - โ โ file:/var/log/nginx/access.log โ 3 โ 3 โ - โ - โ โ file:/var/log/swag/access.log โ 68 โ 68 โ - โ 31 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโฏ Bucket Metrics: โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโฎ โ Bucket โ Current Count โ Overflows โ Instantiated โ Poured โ Expired โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโโค โ crowdsecurity/http-bad-user-agent โ - โ - โ 1 โ 1 โ 1 โ โ crowdsecurity/http-crawl-non_statics โ - โ - โ 19 โ 29 โ 19 โ โ crowdsecurity/http-probing โ - โ - โ 1 โ 1 โ 1 โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโฏ Parser Metrics: โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโโฎ โ Parsers โ Hits โ Parsed โ Unparsed โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโโผโโโโโโโโโโโค โ child-crowdsecurity/http-logs โ 213 โ 150 โ 63 โ โ child-crowdsecurity/nginx-logs โ 71 โ 71 โ - โ โ child-crowdsecurity/syslog-logs โ 1.84k โ 1.84k โ - โ โ crowdsecurity/dateparse-enrich โ 71 โ 71 โ - โ โ crowdsecurity/geoip-enrich โ 71 โ 71 โ - โ โ crowdsecurity/http-logs โ 71 โ 68 โ 3 โ โ crowdsecurity/nginx-logs โ 71 โ 71 โ - โ โ crowdsecurity/non-syslog โ 71 โ 71 โ - โ โ crowdsecurity/syslog-logs โ 1.84k โ 1.84k โ - โ โ crowdsecurity/whitelists โ 71 โ 71 โ - โ โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโฏ Local Api Metrics: โญโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโฎ โ Route โ Method โ Hits โ โโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโผโโโโโโโค โ /v1/alerts โ GET โ 1 โ โ /v1/heartbeat โ GET โ 24 โ โ /v1/watchers/login โ POST โ 3 โ โฐโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโฏ Local Api Machines Metrics: โญโโโโโโโโโโโฌโโโโโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโฎ โ Machine โ Route โ Method โ Hits โ โโโโโโโโโโโโผโโโโโโโโโโโโโโโโผโโโโโโโโโผโโโโโโโค โ crowdsec โ /v1/alerts โ GET โ 1 โ โ crowdsec โ /v1/heartbeat โ GET โ 24 โ โฐโโโโโโโโโโโดโโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโฏ
Look at the latest decisions made:
cscli decisions list
โญโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโฎ โ ID โ Source โ Scope:Value โ Reason โ Action โ Country โ AS โ Events โ expiration โ Alert ID โ โโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโค โ 15006 โ crowdsec โ Ip:184.98.47.230 โ/nginx-req-limit-exceeded โ ban โ US โ 209 CENTURYLINK-US-LEGACโ 6 โ 3h58m33.694712793s โ 7 โ โ 15005 โ crowdsec โ Ip:35.235.116.5 โ/http-bad-user-agent โ ban โ US โ 396982 GOOGLE-CLOUD-PLATโ 2 โ 3h53m38.172330115s โ 6 โ โ 15004 โ crowdsec โ Ip:51.142.82.33 โ/http-bad-user-agent โ ban โ GB โ 8075 MICROSOFT-CORP-MSN-โ 2 โ 3h44m28.590167436s โ 5 โ โ 3 โ crowdsec โ Ip:167.94.138.44 โ/http-bad-user-agent โ ban โ US โ 398324 CENSYS-ARIN-01 โ 2 โ 2h33m42.390288213s โ 3 โ โฐโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโฏ
Step 6: test it!
Test it by banning yourself: take note of your IP address and ban yourself:
cscli decisions add --ip 1.2.3.4 --type ban --duration 10m
Now refresh one of your sites and see for yourself!
Finally, unban yourself:
cscli decisions delete --ip 1.2.3.4 cscli alerts delete --ip 1.2.3.4
You are all set, and protected!
Optional steps to go further
In a future post, I will show you how to easily:
- Increase crowdsec ban time exponentially for repeated offenders
- Test it further with Nikto basic exploit tests
- Notify alerts to your own discord server
- Pair the notification with a MAPQUEST_API_KEY so you even have a map tile with the alert
- Pair it with Google reCaptcha v2 to offer a Captha instead of a ban
- Enroll it with the free Crowdsec dashboard for improved visualization of attacks
- Change the ban message to something more NSFW