May Meow Posts

I Updated My Caddy Stack With CrowdSec

Lately, I decided to enhance the security of my public Caddy server by implementing CrowdSec, an open-source, collaborative security engine. At its core, CrowdSec functions as an IDS/IPS1 powered by crowdsourced threat intelligence.

It analyzes behavior logs from servers in real-time; when an attack is detected, that information is anonymized and instantly shared with the entire network, protecting everyone from emerging threats.

I had looked at other solutions in the past, but I was never quite a fan of them until I found this approach.

The implementation

My Caddy server runs as a Docker container using a customized image. If you want to build it yourself, here is the Dockerfile I use. It is an image I have relied on for a long time; I simply added the CrowdSec-related plugins to the build:

 1FROM caddy:2-builder AS builder
 2
 3RUN xcaddy build \
 4    --with github.com/caddy-dns/cloudflare \
 5    --with github.com/caddy-dns/bunny \
 6    --with github.com/mholt/caddy-l4 \
 7	--with github.com/caddyserver/transform-encoder \
 8	--with github.com/hslatman/caddy-crowdsec-bouncer/http@main \
 9	--with github.com/hslatman/caddy-crowdsec-bouncer/appsec@main \
10	--with github.com/hslatman/caddy-crowdsec-bouncer/layer4@main
11
12FROM caddy:2
13
14COPY --from=builder /usr/bin/caddy /usr/bin/caddy
15
16LABEL org.opencontainers.image.url=https://homelab.0x0.sk

You can find many tutorials online on how to configure the rest, but here is a quick reference of what worked for me.

Challenges

First, you need to ensure Caddy knows where the CrowdSec agent is running. Typically, you would add the following to your global options block:

 1{
 2    crowdsec {
 3        api_url http://crowdsec:8080
 4        api_key {env.CROWDSEC_API_KEY}
 5        ticker_interval 15s
 6    }
 7    log {
 8        output file /var/log/caddy/access.log
 9    }
10}

I included the log directive here because it is crucial that CrowdSec can read the logs. However, this specific setup didn’t work for me. With my version of Caddy (v2), this only generated internal logs without capturing the necessary IP addresses.

I think that configuration is intended for Caddy v1.x. (I found that on some other tutorial, not remember URL).

What finally worked was adding a per-host logging directive. There are multiple ways to achieve this, but I used a snippet import to keep things clean:

1(subdomain-log) {
2    log {
3        hostnames {args[0]}
4        output file /var/log/caddy/{args[0]}.log
5    }
6}

You can import this snippet into specific site blocks. Here is an example from my tinyauth config.

 1tinyauth.redacted-domain.tld {
 2    tls {
 3        dns cloudflare {env.CF_API_TOKEN}            
 4    }
 5
 6    import subdomain-log tinyauth.redacted-domain.tld
 7    route {
 8        crowdsec
 9        reverse_proxy tinyauth:3000 {
10            header_up X-Real-IP {remote_host}
11        }
12    }
13}

Notice the crowdsec directive inside the route2 block? That is where the magic happens. When CrowdSec decides an IP is malicious, the bouncer blocks the request, and the user receives a 403 Forbidden error.

The tests & real world results

I wanted to verify that the setup was actually working, so I ran a simple script to generate some traffic.

⚠️ Warning: You will get blocked if you run this against your own server! Use at your own risk. ⚠️

1for i in {1..50}; do curl -A "BadBot" https://tiny.redacted-domain.tld/random-$i; done

It worked, and after few request I was blocked.

Later that day, I checked the dashboard and saw several other IP addresses that had been caught by the system:

As you can see, CrowdSec doesn’t just detect brute-force attempts; it also recognizes various scanners, CVE exploitation attempts, and other malicious patterns.

So, Now my server is a bit more secure than before. Probably I open my Git instance to the world again.

ℹ️ Disclaimer: I am not affiliated with CrowdSec. This post is based on my personal experience and setup.

☕ Find this useful? Support me on Ko-fi


  1. Intrusion Detection and Prevention System ↩︎

  2. Caddy route docs ↩︎

#Cybersecurity #Security #Linux #Docker