A Caddy module that blocks malicious traffic based on decisions made by CrowdSec.
The Caddy CrowdSec Bouncer consists of five components:
caddy crowdsec Command: Offers useful Caddy CLI commands for your CrowdSec integration.CrowdSec is a free and open source security automation tool that uses local logs and a set of scenarios to infer malicious intent. In addition to operating locally, an optional community integration is also available, through which crowd-sourced IP reputation lists are distributed.
The architecture of CrowdSec is very modular. At its core is the CrowdSec Security Engine, which keeps track of all data and related systems. Bouncers are pieces of software that perform specific actions based on the decisions of the Security Engine.
[!TIP] You can find full setup examples at
examples
You can use the bouncer by either building a custom Caddy image with Docker or by fetching the required Go modules directly into your own build.
Note: You will need a recent version of Caddy (v2.7.3+) and Go (1.20+).
To include the bouncer in a Docker Image using xcaddy. Create a Dockerfile:
ARG CADDY_VERSION=2
FROM caddy:${CADDY_VERSION}-builder-alpine AS builder
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/appsec@main \
--with github.com/hslatman/caddy-crowdsec-bouncer/layer4@main
FROM caddy:${CADDY_VERSION}
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
If you are compiling outside of Docker, you can fetch the modules using go get:
# get the CrowdSec Bouncer HTTP handler
go get github.com/hslatman/caddy-crowdsec-bouncer/http
# get the CrowdSec layer4 connection matcher (only required for TCP/UDP level blocking)
go get github.com/hslatman/caddy-crowdsec-bouncer/layer4
# get the AppSec HTTP handler (only required for CrowdSec AppSec support)
go get github.com/hslatman/caddy-crowdsec-bouncer/appsec
Configuration using a Caddyfile is supported for HTTP handlers and Layer 4 matchers.
| Directive | Description | Default |
|---|---|---|
api_url | The URL of the CrowdSec Local API. | http://127.0.0.1:8080/ |
api_key | The API key to authenticate with the Local API. | <empty> (required) |
disable_streaming | Falls back to LiveBouncer mode (queries API per request). | false |
metrics_interval | Interval for pushing metrics to the Local API. | 0s (disabled) |
enable_caddy_metrics | Enables emitting bouncer metrics at Caddy's /metrics endpoint. | false |
ticker_interval | Interval for pulling decisions from the Local API. | 60s |
enable_hard_fails | Caddy fails to start if CrowdSec API is unreachable. | false |
appsec_url | The URL of the CrowdSec AppSec component. | <empty> (disabled) |
appsec_max_body_bytes | Maximum request body size sent to AppSec. | 0 (full request) |
appsec_max_timeout. | Maximum time for request to AppSec component. | 2s |
appsec_fail_open | Ignore AppSec component connection errors. | false |
enable_caddy_error | Propagates decisions as Caddy errors to allow custom error pages. Warning: Ensure handle_errors routes are strictly static to avoid resource exhaustion (DoS). | false |
When a request is blocked, the HTTP and AppSec handlers record why in the request context, so that Caddyfile matchers and logs can tell blocks apart:
| Variable | Description | Examples |
|---|---|---|
{http.vars.crowdsec.module} | The handler that blocked the request. | http, appsec |
{http.vars.crowdsec.remediation} | The remediation that was applied. | ban, captcha, throttle |
{http.vars.crowdsec.origin} | The origin of the decision | appsec, crowdsec, lists:foo |
{http.vars.crowdsec.duration} | The duration of the decision. | 3h59m58s |
handle_errors {
log_append blocked-by {http.vars.crowdsec.module}
}
{
debug
crowdsec {
api_url http://localhost:8080
api_key <api_key>
ticker_interval 15s
appsec_url http://localhost:7422
#disable_streaming
#enable_hard_fails
#enable_caddy_error
}
layer4 {
localhost:4444 {
@crowdsec crowdsec
route @crowdsec {
proxy {
upstream localhost:6443
}
}
}
}
}
localhost:8443 {
route {
crowdsec
respond "Allowed by Bouncer!"
}
}
localhost:7443 {
route {
appsec
respond "Allowed by AppSec!"
}
}
localhost:6443 {
route {
crowdsec
appsec
respond "Allowed by Bouncer and AppSec!"
}
}
Run the Caddy server
# with a Caddyfile
caddy run --config Caddyfile
This repository also contains an example using Docker inside the examples/demo folder. Steps to run this demo are as follows:
# run CrowdSec container
docker compose up -d crowdsec
# add the Caddy bouncer, generating an API key
docker compose exec crowdsec cscli bouncers add caddy-bouncer
# copy and paste the API key in the ./examples/demo/config.json file
# run Caddy; at first run a custom build will be created using xcaddy
docker compose up -d caddy
# tail the logs
docker compose logs -tf
You can then access https://localhost:9443 and https://localhost:8443. The latter is an example of using the Layer 4 App and will simply proxy to port 9443 in this case.
When Caddy is built with this module enabled, a new caddy crowdsec command will be enabled.
Its subcommands allow you to interact with your CrowdSec integration at runtime using Caddy's Admin API.
This is useful to verify the status of your integration, and check if it's configured and working properly.
The command requires the Admin API to be reachable from the system it is run from.
Support for the command is currently experimental, and will be improved and extended in future releases. Output of the commands should not be relied upon in automated processes (yet).
$ caddy crowdsec
Commands related to the CrowdSec integration (experimental)
Usage:
caddy crowdsec [command]
Available Commands:
check Checks an IP to be banned or not
health Checks CrowdSec integration health
info Shows CrowdSec runtime information
ping Pings the CrowdSec LAPI endpoint
Flags:
-a, --adapter string Name of config adapter to apply (when --config is used)
--address string The address to use to reach the admin API endpoint, if not the default
-c, --config string Configuration file to use to parse the admin address, if --address is not used
-h, --help help for crowdsec
-v, --version version for crowdsec
Use "caddy crowdsec [command] --help" for more information about a command.
Full documentation is available at:
https://caddyserver.com/docs/command-line
When your Caddy server is deployed behind a proxy (like a CDN or load balancer), the actual client IP is masked. Starting with v0.3.1, this module relies on Caddy's native logic (introduced in Caddy v2.7.0 via caddy#5104) to determine the correct client IP before checking it against CrowdSec decisions.
Important: Caddy uses the
X-Forwarded-Forheader by default. To trust this header, you must configure thetrusted_proxiesglobal directive in your Caddyfile.
You can override the default header using the client_ip_headers directive.
Note: For Caddy versions up to v2.4.6 and older versions of this module, the realip module is required.
...and more things to come!
We are always open to contributions! Whether it's fixing bugs, improving documentation, or adding new features from the Roadmap above, your help is welcome. Feel free to open an issue or submit a pull request.
Go
100.0%
A Caddy module that blocks malicious traffic based on decisions made by CrowdSec.
The Caddy CrowdSec Bouncer consists of five components:
caddy crowdsec Command: Offers useful Caddy CLI commands for your CrowdSec integration.CrowdSec is a free and open source security automation tool that uses local logs and a set of scenarios to infer malicious intent. In addition to operating locally, an optional community integration is also available, through which crowd-sourced IP reputation lists are distributed.
The architecture of CrowdSec is very modular. At its core is the CrowdSec Security Engine, which keeps track of all data and related systems. Bouncers are pieces of software that perform specific actions based on the decisions of the Security Engine.
[!TIP] You can find full setup examples at
examples
You can use the bouncer by either building a custom Caddy image with Docker or by fetching the required Go modules directly into your own build.
Note: You will need a recent version of Caddy (v2.7.3+) and Go (1.20+).
To include the bouncer in a Docker Image using xcaddy. Create a Dockerfile:
ARG CADDY_VERSION=2
FROM caddy:${CADDY_VERSION}-builder-alpine AS builder
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/appsec@main \
--with github.com/hslatman/caddy-crowdsec-bouncer/layer4@main
FROM caddy:${CADDY_VERSION}
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
If you are compiling outside of Docker, you can fetch the modules using go get:
# get the CrowdSec Bouncer HTTP handler
go get github.com/hslatman/caddy-crowdsec-bouncer/http
# get the CrowdSec layer4 connection matcher (only required for TCP/UDP level blocking)
go get github.com/hslatman/caddy-crowdsec-bouncer/layer4
# get the AppSec HTTP handler (only required for CrowdSec AppSec support)
go get github.com/hslatman/caddy-crowdsec-bouncer/appsec
Configuration using a Caddyfile is supported for HTTP handlers and Layer 4 matchers.
| Directive | Description | Default |
|---|---|---|
api_url | The URL of the CrowdSec Local API. | http://127.0.0.1:8080/ |
api_key | The API key to authenticate with the Local API. | <empty> (required) |
disable_streaming | Falls back to LiveBouncer mode (queries API per request). | false |
metrics_interval | Interval for pushing metrics to the Local API. | 0s (disabled) |
enable_caddy_metrics | Enables emitting bouncer metrics at Caddy's /metrics endpoint. | false |
ticker_interval | Interval for pulling decisions from the Local API. | 60s |
enable_hard_fails | Caddy fails to start if CrowdSec API is unreachable. | false |
appsec_url | The URL of the CrowdSec AppSec component. | <empty> (disabled) |
appsec_max_body_bytes | Maximum request body size sent to AppSec. | 0 (full request) |
appsec_max_timeout. | Maximum time for request to AppSec component. | 2s |
appsec_fail_open | Ignore AppSec component connection errors. | false |
enable_caddy_error | Propagates decisions as Caddy errors to allow custom error pages. Warning: Ensure handle_errors routes are strictly static to avoid resource exhaustion (DoS). | false |
When a request is blocked, the HTTP and AppSec handlers record why in the request context, so that Caddyfile matchers and logs can tell blocks apart:
| Variable | Description | Examples |
|---|---|---|
{http.vars.crowdsec.module} | The handler that blocked the request. | http, appsec |
{http.vars.crowdsec.remediation} | The remediation that was applied. | ban, captcha, throttle |
{http.vars.crowdsec.origin} | The origin of the decision | appsec, crowdsec, lists:foo |
{http.vars.crowdsec.duration} | The duration of the decision. | 3h59m58s |
handle_errors {
log_append blocked-by {http.vars.crowdsec.module}
}
{
debug
crowdsec {
api_url http://localhost:8080
api_key <api_key>
ticker_interval 15s
appsec_url http://localhost:7422
#disable_streaming
#enable_hard_fails
#enable_caddy_error
}
layer4 {
localhost:4444 {
@crowdsec crowdsec
route @crowdsec {
proxy {
upstream localhost:6443
}
}
}
}
}
localhost:8443 {
route {
crowdsec
respond "Allowed by Bouncer!"
}
}
localhost:7443 {
route {
appsec
respond "Allowed by AppSec!"
}
}
localhost:6443 {
route {
crowdsec
appsec
respond "Allowed by Bouncer and AppSec!"
}
}
Run the Caddy server
# with a Caddyfile
caddy run --config Caddyfile
This repository also contains an example using Docker inside the examples/demo folder. Steps to run this demo are as follows:
# run CrowdSec container
docker compose up -d crowdsec
# add the Caddy bouncer, generating an API key
docker compose exec crowdsec cscli bouncers add caddy-bouncer
# copy and paste the API key in the ./examples/demo/config.json file
# run Caddy; at first run a custom build will be created using xcaddy
docker compose up -d caddy
# tail the logs
docker compose logs -tf
You can then access https://localhost:9443 and https://localhost:8443. The latter is an example of using the Layer 4 App and will simply proxy to port 9443 in this case.
When Caddy is built with this module enabled, a new caddy crowdsec command will be enabled.
Its subcommands allow you to interact with your CrowdSec integration at runtime using Caddy's Admin API.
This is useful to verify the status of your integration, and check if it's configured and working properly.
The command requires the Admin API to be reachable from the system it is run from.
Support for the command is currently experimental, and will be improved and extended in future releases. Output of the commands should not be relied upon in automated processes (yet).
$ caddy crowdsec
Commands related to the CrowdSec integration (experimental)
Usage:
caddy crowdsec [command]
Available Commands:
check Checks an IP to be banned or not
health Checks CrowdSec integration health
info Shows CrowdSec runtime information
ping Pings the CrowdSec LAPI endpoint
Flags:
-a, --adapter string Name of config adapter to apply (when --config is used)
--address string The address to use to reach the admin API endpoint, if not the default
-c, --config string Configuration file to use to parse the admin address, if --address is not used
-h, --help help for crowdsec
-v, --version version for crowdsec
Use "caddy crowdsec [command] --help" for more information about a command.
Full documentation is available at:
https://caddyserver.com/docs/command-line
When your Caddy server is deployed behind a proxy (like a CDN or load balancer), the actual client IP is masked. Starting with v0.3.1, this module relies on Caddy's native logic (introduced in Caddy v2.7.0 via caddy#5104) to determine the correct client IP before checking it against CrowdSec decisions.
Important: Caddy uses the
X-Forwarded-Forheader by default. To trust this header, you must configure thetrusted_proxiesglobal directive in your Caddyfile.
You can override the default header using the client_ip_headers directive.
Note: For Caddy versions up to v2.4.6 and older versions of this module, the realip module is required.
...and more things to come!
We are always open to contributions! Whether it's fixing bugs, improving documentation, or adding new features from the Roadmap above, your help is welcome. Feel free to open an issue or submit a pull request.
Go
100.0%