Protect a Django Server With CrowdSec

Set up CrowdSec to protect a Django server: install the agent and Nginx or firewall bouncer, parse Nginx and Django login logs, detect brute force on /admin and login views, share blocklists, and avoid banning legitimate users behind proxies.

Problem statement

Your Django server's logs show constant scanning: requests for /wp-login.php, .env files, and repeated failed logins against /admin/. fail2ban can ban IPs from log patterns, but you write and maintain every rule yourself and only see attacks on your own server. CrowdSec parses logs with community-maintained scenarios, bans offenders through bouncers (firewall, Nginx, Cloudflare), and shares signals so you also block IPs that attacked other servers.

CrowdSec for DjangoLogsNginx, Django authAgentparsers + scenariosDecisionsban for durationBouncerfirewall or NginxThe agent decides who to ban from logs; bouncers enforce it.
  1. Install CrowdSec from its repository; it detects Nginx and SSH logs automatically.
  2. Add collections: crowdsecurity/nginx, crowdsecurity/http-cve, and crowdsecurity/base-http-scenarios.
  3. Install a bouncer: crowdsec-firewall-bouncer-nftables (or the Nginx bouncer).
  4. Log failed Django logins with the client IP and add a custom scenario for login brute force.
  5. Make sure logs contain the real client IP when you are behind a proxy or CDN, and whitelist your own addresses.

Step-by-step solution

1. Install the agent

curl -s https://install.crowdsec.net | sudo sh
sudo apt-get install -y crowdsec
sudo cscli collections install crowdsecurity/nginx crowdsecurity/base-http-scenarios crowdsecurity/http-cve
sudo systemctl reload crowdsec

Read the install script before running it on production. cscli metrics shows which log files are read and how many lines are parsed. The HTTP scenarios detect probing for sensitive paths, bad user agents, and known CVE exploit attempts.

2. Install a bouncer

sudo apt-get install -y crowdsec-firewall-bouncer-nftables
sudo cscli bouncers list

The firewall bouncer drops traffic from banned IPs at the kernel level before it reaches Nginx or Gunicorn. The Nginx bouncer (Lua-based) can show a captcha instead of a hard ban. Behind Cloudflare, the Cloudflare bouncer pushes decisions to Cloudflare's firewall, because your server only sees Cloudflare's IPs.

where should bans apply?What sits in front of Django?Nginx on the serverFirewall bouncernftables dropswant captcha, not banNginx bouncerLua moduleCloudflare proxyCloudflare bounceredge firewallEnforce bans where the attacker’s real IP is visibledjango-deployment.com
A firewall bouncer behind a CDN would ban the CDN, not the attacker.

3. Log Django login failures

Nginx logs show every request but not whether a login succeeded. Log failures from Django:

import logging
from django.contrib.auth.signals import user_login_failed
from django.dispatch import receiver

auth_logger = logging.getLogger("django.auth.failures")

@receiver(user_login_failed)
def log_failed_login(sender, credentials, request, **kwargs):
    ip = request.META.get("REMOTE_ADDR") if request else "-"
    auth_logger.warning("login failed ip=%s user=%s", ip, credentials.get("username", "-"))

Write this logger to a file such as /var/log/shop/auth.log (or journald) and add it to CrowdSec's acquisition:

# /etc/crowdsec/acquis.d/django.yaml
filenames:
  - /var/log/shop/auth.log
labels:
  type: django-auth

4. Add a scenario for login brute force

Create a parser that extracts ip from type: django-auth lines, and a leaky-bucket scenario:

# /etc/crowdsec/scenarios/django-login-bf.yaml
type: leaky
name: local/django-login-bf
description: "Brute force on Django login"
filter: "evt.Meta.log_type == 'django_failed_auth'"
groupby: evt.Meta.source_ip
capacity: 5
leakspeed: "1m"
blackhole: 5m
labels:
  remediation: true

This bans an IP after more than five failures in a short period. Test with cscli explain --file /var/log/shop/auth.log --type django-auth.

5. Get the real client IP and whitelist yourself

Behind a load balancer or Cloudflare, REMOTE_ADDR and Nginx's $remote_addr are the proxy's address unless you configure the real IP module (set_real_ip_from, real_ip_header). Banning the proxy blocks everyone. Add your office, VPN, and monitoring IPs to a whitelist parser so health checks and staff are never banned.

before trusting automatic bansReal client IP in all logsreal_ip moduleNginx and HTTP collectionscscli metrics shows parsingDjango failed logins loggedcustom scenarioBouncer where IP is visiblefirewall or CloudflareWhitelist staff and monitorsavoid self-bansWrong client IPs turn a security tool into an outagedjango-deployment.com
Run in simulation mode first to see what would be banned.

Explanation

CrowdSec separates detection from enforcement. The agent reads logs, normalizes them with parsers, and feeds events into scenarios, mostly leaky buckets that overflow when too many matching events arrive from one source. Overflows produce decisions (ban, captcha) stored in the local API, and bouncers pull those decisions and enforce them. Opting into the community blocklist adds IPs reported by other CrowdSec users, so known attackers are blocked before they reach you.

Edge cases / notes

  • Shared IPs. Corporate NATs and mobile carriers put many users behind one IP; keep ban durations short for HTTP scenarios.
  • Containers. Run CrowdSec on the host reading container logs, or as a container with access to the log files; bouncers must act on the host firewall or proxy.
  • Django rate limiting. CrowdSec complements, not replaces, app-level protection like django-axes or rate limits.

Next steps

FAQ

Is CrowdSec a replacement for fail2ban?

It covers the same job with community scenarios, multiple bouncer types, and shared blocklists. Many teams switch, some run both briefly.

Can CrowdSec detect Django admin brute force?

With a log of failed logins and a custom scenario, yes. Nginx logs alone cannot tell failed from successful logins.

Will CrowdSec block legitimate users?

It can if logs show proxy IPs instead of real clients or if staff IPs are not whitelisted. Fix both before enforcing.

Does it work behind Cloudflare?

Yes, with the Cloudflare bouncer, which applies bans at Cloudflare's edge.

Learning path · step 10 of 15

Hardening a Django deployment

Keep going