Turning raw ban logs into a live, self‑updating proof of resilience - without leaking a single op‑sec detail.
The Invisible Shield
I wanted the world to see who was trying to get in - not just me via CrowdSec or Fail2Ban logs.
Attackers hammer away at a few of my sites, from my portfolio (beane.me) to maggie.lol, a tribute to my two Golden Retrievers. This post turns that invisible defense into something legible.
The Problem With Raw Logs
Unfiltered cscli alerts are great for operators, but most readers need a clean, scannable summary. A public dashboard makes the defense real - at a glance - without exposing implementation details.
Designing the Pipeline
- Detection: CrowdSec + Fail2Ban
- Fetcher: Python → JSON (sanitized, deduped)
- Renderer: Python → HTML (atomic writes)
- Scheduler: cron (details redacted for op‑sec)
Fetching and Sanitizing Alerts
Using Python, I shell out to cscli, parse JSON, normalize timestamps, and cap to the most recent 25 decisions to keep the feed fast and readable.
import json, subprocess
def fetch_all_alerts():
"""Call cscli and return parsed alerts JSON."""
res = subprocess.run(
["cscli", "alerts", "list", "-o", "json"],
capture_output=True, text=True, check=True
)
return json.loads(res.stdout) if res.stdout else []
def dedup_merge(existing, recent, cap=25):
keys = {(d["ip"], d["start_at"]) for d in existing if "ip" in d and "start_at" in d}
new_items = [d for d in recent if (d["ip"], d["start_at"]) not in keys]
combined = new_items + existing
combined.sort(key=lambda d: d["start_at"], reverse=True)
return combined[:cap]We call cscli as a subprocess to retrieve the current alerts, output them in JSON, and feed them into our pipeline. In production, a simple try/except guards against malformed output - omitted here for brevity.
Rendering the Dashboard
A scheduled Python job parses the refreshed JSON and publishes updates to threats.beane.me every 15 minutes.
Security-Aware Storytelling
A good public dashboard shows the impact, not the implementation. I’ve omitted cron lines, usernames, and absolute paths - proving you can be transparent without handing out a blueprint.
Before → After
Raw cscli output
ID value reason country as decisions created_at
5119 Ip:4.190.198.107 http-admin-interface-probing JP MICROSOFT-CORP-MSN-AS-BLOCK ban:1 2025-08-14 13:19 UTCWhat's Next
Next up: intel.beane.me - taking the same automation‑driven clarity you see here and aiming it at the broader threat landscape, so every signal tells a story.
