1. The problem I’m having:
I’m trying to display a block message if a subdomain was blocked by Crowdsec.
The blocking seems to work but I don’t get a message.
2. Error messages and/or full log output:
curl -vkL test2.domain.de
* Host test2.domain.de:80 was resolved.
* IPv6: (none)
* IPv4: 91.65.81.193
* Trying 91.65.81.193:80...
* Connected to test2.domain.de (91.65.81.193) port 80
* using HTTP/1.x
> GET / HTTP/1.1
> Host: test2.domain.de
> User-Agent: curl/8.11.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 308 Permanent Redirect
< Connection: close
< Location: https://test2.domain.de/
< Server: Caddy
< Date: Fri, 28 Mar 2025 07:08:47 GMT
< Content-Length: 0
<
* shutting down connection #0
* Clear auth, redirects to port from 80 to 443
* Issue another request to this URL: 'https://test2.domain.de/'
* Host test2.domain.de:443 was resolved.
* IPv6: (none)
* IPv4: 91.65.81.193
* Trying 91.65.81.193:443...
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256 / x25519 / id-ecPublicKey
* ALPN: server accepted h2
* Server certificate:
* subject: CN=test2.domain.de
* start date: Mar 25 19:37:43 2025 GMT
* expire date: Jun 23 19:37:42 2025 GMT
* issuer: C=US; O=(STAGING) Let's Encrypt; CN=(STAGING) Pseudo Plum E5
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384
* Certificate level 1: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using sha256WithRSAEncryption
* Connected to test2.domain.de (91.65.81.193) port 443
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://test2.domain.de/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: test2.domain.de]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.11.1]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: test2.domain.de
> User-Agent: curl/8.11.1
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Request completely sent off
< HTTP/2 200
< alt-svc: h3=":443"; ma=2592000
< content-type: text/plain; charset=utf-8
< date: Fri, 28 Mar 2025 07:08:47 GMT
< server: Caddy
< content-length: 286
<
Hostname: 095ea83ffeb6
IP: 127.0.0.1
IP: ::1
IP: 192.168.96.3
RemoteAddr: 192.168.96.16:42066
GET / HTTP/1.1
Host: test2.domain.de
User-Agent: curl/8.11.1
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 91.65.81.193
X-Forwarded-Host: test2.domain.de
X-Forwarded-Proto: https
* Connection #1 to host test2.domain.de left intact
3. Caddy version: v2.9.1
4. How I installed and ran Caddy: docker compose
a. System environment:
b. Command:
PASTE OVER THIS, BETWEEN THE ``` LINES.
Please use the preview pane to ensure it looks nice.
c. Service/unit/compose file:
version: "3.8"
# used for specific settings you have outside of your docker config
# ex: proxies to external servers, storage configuration...
# remove this block entirely if not needed (Only used for Docker Swarm)
configs:
caddy-basic-content:
file: ./caddy/Caddyfile
labels:
caddy: null
services:
caddy:
image: ghcr.io/serfriz/caddy-crowdsec-geoip-ratelimit-security-dockerproxy:latest
container_name: caddy
ports:
- 80:80
- 443:443
environment:
CADDY_INGRESS_NETWORKS: caddy
CROWDSEC_API_KEY: ${CROWDSEC_API_KEY}
DEBUG: true
CADDY_DOCKER_CADDYFILE_PATH: /etc/caddy/Caddyfile
networks:
- caddy
- crowdsec
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./caddy/data:/data
- ./caddy/logs:/logs
- ./caddy/config:/config
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- ./geolite/GeoLite2-Country.mmdb:/etc/caddy/GeoLite2-Country.mmdb
security_opt:
- no-new-privileges=true
crowdsec:
image: crowdsecurity/crowdsec:latest
container_name: crowdsec
restart: unless-stopped
ports:
- 8080:8080
volumes:
- ./crowdsec/db:/var/lib/crowdsec/data/
- ./caddy/logs:/var/log/caddy:ro
- ./crowdsec:/etc/crowdsec
environment:
- CROWDSEC_LOG_LEVEL=info
- GID=1000
- COLLECTIONS=crowdsecurity/caddy crowdsecurity/http-cve
crowdsecurity/whitelist-good-actors
- BOUNCER_KEY_CADDY=${CROWDSEC_API_KEY}
networks:
- caddy
- crowdsec
security_opt:
- no-new-privileges=true
d. My complete Caddy config:
{
debug
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
crowdsec {
api_url http://crowdsec:8080
api_key ${CROWDSEC_API_KEY}
}
# Global settings for all sites and subdomains
order crowdsec first # Ensures CrowdSec runs early in the request chain
}
(crowdsec_block) {
crowdsec {
block_message "Access denied: Your IP has been blocked by CrowdSec."
response_status 403
}
}
test.domain.de {
import crowdsec_block
reverse_proxy whoami1
}
test2.domain.de {
log {
output file /logs/test2-access.log
}
import crowdsec_block
reverse_proxy whoami1
}