hslatman / caddy-crowdsec-bouncer

A Caddy module that blocks malicious traffic based on decisions made by CrowdSec.
126 stars 4 forks source link

crowdsec unrecognized directive in Caddyfile #45

Open AlpenFlizzer opened 2 days ago

AlpenFlizzer commented 2 days ago

I have Caddy running in Docker and originally installed the crowdsec plugin using

RUN xcaddy build \
    --with github.com/hslatman/caddy-crowdsec-bouncer/crowdsec

(note that this is the snippet provided at the caddy documentation for modules - and also it should have sufficed my use case since I only use reverse_proxy handlers, no L4)

Then I added the Crowdsec Config in the global block of my Caddyfile as given in your and crowdsec's guides. The communication between LAPI on my Firewall and the caddy-crowdsec-bouncer worked and I could verify that I got decisions from the LAPI to the bouncer. However, in order to make Caddy execute the decisions i.e. by blocking a banned IP, it is necessary to add the crowdsec directive inside a route block for a specific site configuration. At this point, when adding the crowdsec directive inside a route block, Caddy would not start, but give me an error that crowdsec is an unrecognized directive.

After one day of troubleshooting, I decided to rebuild Caddy with the following snippet:

RUN xcaddy build \
    --with github.com/mholt/caddy-l4 \
    --with github.com/caddyserver/transform-encoder \
    --with github.com/hslatman/caddy-crowdsec-bouncer/http@main \
    --with github.com/hslatman/caddy-crowdsec-bouncer/layer4@main \
    --with github.com/hslatman/caddy-crowdsec-bouncer/crowdsec

Long story short, after that, crowdsec was recognized as directive and I could verify, banned IPs were blocked by crowdsec before reverse proxying.

I wanted to ask, what is the reason it did not work with the first snippet, but with the second. Furthermore I want to suggest to provide this information somehow to assist anyone else experiencing this issues. Maybe your or Crowdsec's guides could be extended or adapted to include some further information about this specific behaviour.

Thank you for your nice work on the caddy bouncer!

All the best!

Btw. my working Dockerfile and Caddyfile:

#Docekrfile
FROM caddy:builder AS builder

RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare \
    --with github.com/mholt/caddy-l4 \
    --with github.com/caddyserver/transform-encoder \
    --with github.com/hslatman/caddy-crowdsec-bouncer/http@main \
    --with github.com/hslatman/caddy-crowdsec-bouncer/layer4@main \
    --with github.com/hslatman/caddy-crowdsec-bouncer/crowdsec

FROM caddy:latest

COPY --from=builder /usr/bin/caddy /usr/bin/caddy
#Caddyfile
{
    acme_dns cloudflare XXXXXXXXXXXXXXXXXXXXXXXXXXX

    crowdsec {
        api_key XXXXXXXXXXXXXXXXXXXXXXXXXXX
        api_url http://LAPI:port/
        ticker_interval 10s
    }
}

test.example.com {
    log {
        level INFO
        format console
        output file /var/log/caddy/test1-access.log {
            roll_size 10MB
            roll_keep 10
        }
    }

    encode gzip

    header /* {
        Strict-Transport-Security "max-age=31536000;"
        X-XSS-Protection "1; mode=block"
        X-Robots-Tag "none"
        X-Content-Type-Options "nosniff"
        X-Content-Security-Policy "default-src 'self'"
        Permissions-Policy "microphone=(), geolocation=()"
    }

    route {
        crowdsec
        reverse_proxy targethost:port {
            header_up X-Real-IP {remote_host}
        }
    }
}

Regarding the docker-compose.yaml the guide at https://github.com/crowdsecurity/example-docker-compose/tree/main/caddy works well. I just used environment variables to configure crowdsec on this machine as log processor only. Found the information about the variables at https://hub.docker.com/r/crowdsecurity/crowdsec

hslatman commented 2 days ago

Hi @AlpenFlizzer,

The automatically generated Caddy documentation shows the docs for github.com/hslatman/caddy-crowdsec-bouncer/crowdsec because that's a Caddy module for a Caddy app. However, that itself does not include the http.Handler Caddy module that's in github.com/hslatman/caddy-crowdsec-bouncer/http (which also exists in the autogenerated Caddy documentation: https://caddyserver.com/docs/modules/http.handlers.crowdsec). The app module provides shared configuration, logic and storage for multiple HTTP handlers. And it does the same for the layer4 module, which you only need if you want it to work on the TCP/UDP level too.

The usage of two modules in the custom build is shown and explained in https://github.com/hslatman/caddy-crowdsec-bouncer?tab=readme-ov-file#usage. In fact, importing just github.com/hslatman/caddy-crowdsec-bouncer/http is sufficient, as it depends on github.com/hslatman/caddy-crowdsec-bouncer/crowdsec for shared functionality, so it will automatically import the app module too. That is also what happens in https://github.com/crowdsecurity/example-docker-compose/blob/main/caddy/Dockerfile.

You're not the first one to hit this case; this one was relatively recent: https://github.com/hslatman/caddy-crowdsec-bouncer/issues/44. I'll see if I can improve the docs by 1) showing a full xcaddy example, and 2) mentioning somewhere in the app module docs that the http.handlers.crowdsec module is needed to for this to do some actual work, so that it will show up on the Caddy docs. It might also be possible to log a warning or an error at startup when crowdsec is loaded successfully, but http.handlers.crowdsec is not, so that at least it's clear that the configuration is not fully functional yet. I'm not sure if the crowdsec is an unrecognized directive error message can be improved. It would be nice to indicate to a user then need the http.handlers.crowdsec module in that case.