---
title: "Automating IP Bans with Cloudflare WAF, CrowdSec and AbuseIPDB"
description: "Automating malicious IP bans with CrowdSec, Cloudflare WAF and AbuseIPDB: a full pipeline to protect a self-hosted homelab without manual intervention."
url: "https://www.arleo.eu/en/posts/crowdsec-cloudflare-waf-autoban/"
language: "en"
datePublished: "2026-04-10T00:23:00+02:00"
dateModified: "2026-05-15T23:51:25+02:00"
tags: ["network","cloudflare","crowdsec","infrastructure","homelab"]
categories: ["infrastructure"]
contentSignal: "ai-train=no, search=yes, ai-input=yes"
---

# Automating IP Bans with Cloudflare WAF, CrowdSec and AbuseIPDB
Automating malicious IP bans with CrowdSec, Cloudflare WAF and AbuseIPDB: a full pipeline to protect a self-hosted homelab without manual intervention.



## ⚡ In short

Passive monitoring is not enough. This pipeline automates **closing the loop in under 5 minutes** between an attack detected by Cloudflare WAF and the effective ban of the IP in CrowdSec, its synchronization to Cloudflare, and its report to AbuseIPDB. A Python script polls the Cloudflare GraphQL API every 5 minutes, applies a 3-hit threshold, and triggers the ban with recidivist escalation.

## 🧠 Why

Seeing an attack in BetterStack logs after the fact does not stop the malicious IP from continuing to hammer the server. Without automation, the detection → ban loop takes hours or never closes. Cloudflare WAF actions (`block`, `challenge`, `managed_challenge`, `jschallenge`) are clear attack signals, but they remain confined to the Cloudflare dashboard — without a bridge to CrowdSec, no IP is banned locally, none is reported to the AbuseIPDB community.

## 🔧 What was done

### Pipeline Architecture

```
Cloudflare WAF (block / challenge / managed_challenge)
        ↓  poll every 5 minutes (GraphQL API)
crowdsec-cf-sync.py  →  threshold: 3 hits / 5 min per IP
        ↓
CrowdSec (cscli decisions add)  →  recidivist escalation
        ↓
Cloudflare IP Access Rules (synchronized ban)
        ↓
AbuseIPDB (report categories 19 + 21)
        ↓
BetterStack (enriched log in crowdsec-decisions source)
```

### 1. Cloudflare WAF Event Collection

The `poll_cloudflare_waf()` function queries the Cloudflare GraphQL API every **5 minutes** and retrieves events with the following actions:

- `block`
- `challenge`
- `managed_challenge`
- `jschallenge`

A state file (`cf_waf_state.json`) stores the timestamp of the last processed event to avoid duplicates between cycles.

### 2. Detection and Threshold

Events are grouped by source IP. A ban is triggered only if an IP accumulates **3 or more hits within a 5-minute sliding window**. This threshold avoids false positives from legitimate challenges (monitoring bots, declared scanners such as Palo Alto Xpanse, etc.).

### 3. Allowlist

The existing CrowdSec allowlist (`my_allowlist`) is checked before any ban. It includes monitoring bot IPs, Cloudflare and BetterStack ranges. No allowlisted IP can be banned by this mechanism.

### 4. Ban Escalation (Recidivists)

The system applies the same escalation logic as other ban sources:

| Occurrence | Ban Duration |
|---|---|
| 1st ban | 4 hours |
| 2nd ban | 24 hours |
| 3rd ban and beyond | 168 hours (7 days) |

The recidivism counter is shared with classic CrowdSec bans via `recidivists.json`.

### 5. AbuseIPDB Reporting

Each banned IP is automatically reported to AbuseIPDB with:

- **Category 19**: Web Application Attack
- **Category 21**: Web App Scan
- A comment including the Cloudflare hit count and targeted URIs

### 6. BetterStack Logging

Each ban generates an enriched entry in the `crowdsec-decisions` source with specific fields:

```json
{
  "source": "cloudflare_waf",
  "cf_action": "managed_challenge",
  "hit_count": 23,
  "uris_targeted": ["/wp.php", "/shell.php", "/backdoor.php"],
  "cs_scenario": "cloudflare-waf/23-hits",
  "cs_duration": "168h"
}
```

### Real Example — Night of April 9, 2026

A massive PHP webshell scan was detected and automatically neutralized:

| Field | Value |
|---|---|
| Source IP | Azure France |
| Hits detected | 23 in < 1 minute |
| Targeted URIs | `/wp.php`, `/shell.php`, `/gpt-sh.php`, `/wp-content/plugins/hellopress/wp_filemanager.php`… |
| Cloudflare action | `managed_challenge` (Bot Fight Mode) |
| Ban applied | 168h (recidivist) |
| AbuseIPDB score | 100% |
| Detection → ban delay | < 5 minutes |

None of these resources exist on the server (Grav CMS, not WordPress).

### Files and Services

| Component | Location |
|---|---|
| Main script | `/usr/local/bin/crowdsec-cf-sync.py` |
| WAF state | `/var/log/crowdsec/cf_waf_state.json` |
| Recidivists | `/var/log/crowdsec/recidivists.json` |
| Systemd service | `crowdsec-cf-sync.service` |
| Vector config | `/etc/vector/vector.yaml` |

### Useful Commands

```bash
# Monitor WAF bans in real time
journalctl -fu crowdsec-cf-sync | grep -i "waf\|cloudflare"

# View recidivists
cat /var/log/crowdsec/recidivists.json | python3 -c "
import json,sys
d=json.load(sys.stdin)
for ip,v in sorted(d.items(), key=lambda x: x[1]['count'], reverse=True):
    print(f'{ip:20} count={v[\"count\"]} last={v[\"last_seen\"]}')
"

# View last WAF poll state
cat /var/log/crowdsec/cf_waf_state.json | python3 -m json.tool

# Restart the service
systemctl restart crowdsec-cf-sync
```

## 🏁 Conclusion

This pipeline reduces the response time to an attack from hours to under 5 minutes. Aggressive IPs are banned locally, synchronized to Cloudflare, and reported to the AbuseIPDB community automatically without manual intervention. Recidivist escalation ensures persistent attackers accumulate increasingly long bans.

**To go further:**

- 💡 Add VirusTotal integration to score IPs before reporting to AbuseIPDB
- 💡 Implement a dedicated BetterStack dashboard with alerts on WAF ban spikes



## Tags

- network
- cloudflare
- crowdsec
- infrastructure
- homelab

## Categories

- infrastructure
