Skip to content

🛡️ CrowdSec

CrowdSec acts as the intelligence hub for the server. It reads logs, detects attacks (brute force, scanners, botnets), and issues "Ban" decisions. It does not block traffic itself; it instructs Caddy (the "Bouncer") to do the blocking.


Compose File

The container is configured to persist its database locally (for backups) and read Caddy's logs in Read-Only mode.

networks:
  dockerapps-net:
    external: true

services:
  crowdsec:
    image: crowdsecurity/crowdsec:latest
    container_name: crowdsec
    hostname: crowdsec
    networks:
      dockerapps-net:
        ipv4_address: 172.20.0.24
        ipv6_address: fd00:dead:beef:2::24
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
      # ADDED: additional collections and scenarios
      - COLLECTIONS=crowdsecurity/caddy crowdsecurity/http-cve crowdsecurity/base-http-scenarios crowdsecurity/whitelist-good-actors crowdsecurity/linux LePresidente/jellyfin 
      - SCENARIOS=crowdsecurity/http-probing LePresidente/jellyfin-bf baudneo/gotify-bf crowdsecurity/http-generic-bf
    volumes:
      - ./config:/etc/crowdsec:rw
      - ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro
      - /mnt/pool01/homelab/services/gateway-stack/caddy/logs:/var/log/caddy:ro
      #Mount Jellyfin's Logs - note to add filename log to acquis.yaml too
      - /mnt/pool01/homelab/services/media-stack/jellyfin/jellyfin-config/log:/var/log/jellyfin:ro
      - ./data:/var/lib/crowdsec/data/
      # Added Docker Socket to monitor VoidAuth's instance - not yet configured with Crowdsec
      - /var/run/docker.sock:/var/run/docker.sock:ro
    restart: unless-stopped

Compose Notes

  • COLLECTIONS: Added a few more to handle generic web threats (crawlers, scanners) and LePresidente/jellyfin for application-specific parsing.

  • SCENARIOS: Implemented LePresidente/jellyfin-bf for brute-force detection and crowdsecurity/http-probing to catch bots scanning for sensitive files.

  • Log Mounting: Mounted the internal Jellyfin log path (/log:/var/log/jellyfin:ro) to allow CrowdSec to monitor internal application events that Caddy cannot see.


Acquisition Configuration (acquis.yaml)

These files tell CrowdSec specifically which files to tail and how to interpret them. Without this, CrowdSec runs blind.

Create file @ crowdsec/acquis.yaml

# Caddy Access Logs
filenames:
  - /var/log/caddy/access.log
labels:
  type: caddy
---
# Jellyfin Logs
filenames:
  - /var/log/jellyfin/log_*.log
labels:
  type: jellyfin

Acquis Notes

  • Added Jellyfin's logs for denied entry loggings to ban. Instead of the broad .log wildcard, switched to log_.log. This prevents CrowdSec from attempting to parse FFmpeg transcode logs, which contain technical data but not relevant.
Expand to view acquis file if using Authentik

If using Authentik: Create new File: crowdsec/config/acquis.d/authentik.yaml

# Watch the Authentik's instance via the Docker container
source: docker
container_name:
  - authentik-server
labels:
  type: authentik

Integration Workflow

CrowdSec requires a "Bouncer" API key to allow Caddy to check IP reputations.

Generate Bouncer API Key

If we rebuild the server, the old API key in Caddy's .env will be invalid. We must generate a new one:

  1. Start CrowdSec:
  2. Run the Registration Command:
    docker exec crowdsec cscli bouncers add caddy-bouncer
    
  3. Copy the Key: The output will be Api key for 'caddy-bouncer': <KEY>
  4. Update Caddy: Paste this key into /mnt/pool01/homelab/services/gateway-stack/caddy/.env as CROWDSEC_API_KEY.
  5. Restart Caddy: docker compose restart caddy

Auto-Update Hub

Using Systemd Timer to ensure detection rules (Hub) remain fresh between software upgrades. This prevents the "stale rules" vulnerability.

The Service (.service)

Create New File: /etc/systemd/system/crowdsec-hub-update.service:

[Unit]
Description=Update CrowdSec Hub Rules and Parsers
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
# Chain the update and upgrade commands safely inside the container
ExecStart=/usr/bin/docker exec crowdsec sh -c "cscli hub update && cscli hub upgrade"
The Timer (.timer)

Create New File: /etc/systemd/system/crowdsec-hub-update.timer:

[Unit]
Description=Weekly timer for CrowdSec Hub updates

[Timer]
# Run every Sunday at 04:15:00
OnCalendar=Sun *-*-* 04:15:00
# If the system was off, run immediately on boot
Persistent=true
# Add delay to prevent exact-second spikes if schedule miss due to shutdown system
RandomizedDelaySec=600

[Install]
WantedBy=timers.target

Enable & Activate:

# Reload systemd to recognize new files
sudo systemctl daemon-reload

# Enable and start the timer
sudo systemctl enable --now crowdsec-hub-update.timer

# Verify the schedule
systemctl list-timers | grep crowdsec

Security Layers & Cloudflare Sync

While CrowdSec handles the "Brain" locally, our Cloudflare WAF acts as the first filter at the Edge.

Layer Component Function
Edge Cloudflare WAF Blocks non-SG traffic and known bots before they hit your home IP.
Origin Caddy + MaxMind Redundant Geo-IP check if traffic bypasses the Cloudflare proxy.
Logic CrowdSec Parses successful connections for behavioral threats (eg: brute force).

Operational Commands (Cheatsheet)

Check Status: See if CrowdSec is successfully reading logs and parsing them.

docker exec crowdsec cscli metrics

Look for: Acquisition Metrics (Lines read) and Parser Metrics (Parseds); Local API Alerts and Local API Bouncers Metrics

Check Community's Decision's Blocklist Count: See if CrowdSec is successfully getting pool of blocklists from upstream.

docker exec crowdsec cscli decisions list --origin CAPI | wc -l

Output should in 10K-20K - this confirms server is successfully downloading the "Community Blocklist" (CAPI). If this connection were broken, this number would be 0.

Status

Verify Parsing: Ensure CrowdSec is seeing the new traffic from our tests.

docker exec crowdsec cscli hub list | grep "caddy"

List Active Bans: See who is currently blocked.

docker exec crowdsec cscli decisions list

Status

Manually Ban an IP: Useful for testing or blocking a persistent pest.

docker exec crowdsec cscli decisions add --ip <IP_ADDRESS> --duration 24h --reason "manual ban"

Unban an IP:

docker exec crowdsec cscli decisions delete --ip <IP_ADDRESS>

Update Collections: Update the threat intelligence rules.

docker exec crowdsec cscli hub update
docker exec crowdsec cscli hub upgrade

Check CAPI Status:

docker exec crowdsec cscli capi status

Output should be like so:

Status