flg
(Fl)
June 27, 2025, 7:58pm
1
1. The problem I’m having:
I am trying to get the Maxmind Geoblocking to work but I am having trouble finding the correct Caddyfile Syntax.
According to logs the IPs are getting recognized as not allowed, but blocking is not working.
2. Error messages and/or full log output:
{"level":"debug","ts":1750965926.6331162,"logger":"http.matchers.maxmind_geolocation","msg":"Detected MaxMind data","ip":"188.214.XXX","country":"MA","subdivisions":"","metro_code":0,">
{"level":"debug","ts":1750965926.6332605,"logger":"http.matchers.maxmind_geolocation","msg":"Country not allowed","country":"MA"}
3. Caddy version:
v2.10.0 h1:fonubSaQKF1YANl8TXqGcn4IbIRUDdfAkpcsfI/vX5U=
4. How I installed and ran Caddy:
a. System environment:
Caddy on Docker on Ubuntu built with xcaddy:
–with github.com/hslatman/caddy-crowdsec-bouncer/http
–with github.com/hslatman/caddy-crowdsec-bouncer/appsec
–with GitHub - porech/caddy-maxmind-geolocation: Caddy v2 module to filter requests based on source IP geolocation
b. Command:
docker compose
c. Service/unit/compose file:
d. My complete Caddy config:
{
order crowdsec after error # forces the CrowdSec directive to be executed after geoblock
## CROWDSEC ##
crowdsec {
api_url http://crowdsec:8080
api_key 999999999
appsec_url http://crowdsec:7422
}
}
## GLOBAL SNIPPETS ##
(auth) {
reverse_proxy /outpost.goauthentik.io/* authentik_server:9000 {
header_up Host {http.reverse_proxy.upstream.host}
}
forward_auth authentik_server:9000 {
uri /outpost.goauthentik.io/auth/caddy
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version
trusted_proxies private_ranges
}
}
(geoblock) {
@geofilter {
not maxmind_geolocation {
db_path "/usr/local/etc/geoip/GeoLite2-Country.mmdb"
allow_countries DE
}
}
error @geofilter 403
}
## SITE CONFIG ##
test.test.de {
import security-header
import geoblock
route {
crowdsec
#appsec
reverse_proxy authentik_server:9443 {
transport http {
tls_insecure_skip_verify
}
}
}
}
5. Links to relevant resources:
timelordx
(timelordx)
June 27, 2025, 10:16pm
2
You can check the directive order in the Caddy documentation here:
Since route
has a higher priority than error
, it will always take precedence.
You might want to try something like this:
(geoblock) {
@geofilter {
not maxmind_geolocation {
db_path "/usr/local/etc/geoip/GeoLite2-Country.mmdb"
allow_countries DE
}
}
handle @geofilter {
error 403
}
}
flg
(Fl)
June 28, 2025, 10:01am
3
Thanks!
Now blocking works. New problem is now, that my real IP is not getting recognized but the internal IP:
>
{"level":"debug","ts":1751104956.4746811,"logger":"http.matchers.maxmind_geolocation","msg":"Detected MaxMind data","ip":"172.18.0.1","country":"","subdivisions":"","metro_code":0,"asn":0}
{"level":"debug","ts":1751104956.4747846,"logger":"http.matchers.maxmind_geolocation","msg":"Country not allowed","country":""}
Any idea how to fix this?
timelordx
(timelordx)
June 28, 2025, 4:24pm
4
You haven’t shared how you’re running Caddy in Docker, so I’m going to assume you’re using bridge mode. Try switching it to the host network.
That said, without seeing your docker-compose
file or the exact Docker command you’re using, I’m really just guessing here.
flg
(Fl)
June 29, 2025, 8:14am
5
You are completly right. Yes it is running in docker. Heres my compose:
services:
caddy:
build:
context: .
dockerfile: Dockerfile
image: caddy-crowdsec-geoip
container_name: caddy
restart: unless-stopped
networks:
- proxy
ports:
- 80:80
- 443:443
environment:
- CROWDSEC_API_KEY=XXXXXXXXXX
volumes:
- ./data:/data
- ./config:/config
- /home/srv:/srv
- /home//geoip/:/usr/local/etc/geoip
- ./conf:/etc/caddy
- ./log:/var/log
networks:
proxy:
external: true
I think switchting to host network mode would not be a good solution as I am loosing the docker name resolutions (which I use in the config for crowdsec etc).
The proxy network has every other service which I want to expose to caddy.
What I dont understand: As per my initial post the IPs got recognized correctly. The only thing I changed was adding the correct error handle and adding X-Client-IP.
This is the current Caddyfile:
(geoblock) {
@geofilter {
not maxmind_geolocation {
db_path "/usr/local/etc/geoip/GeoLite2-Country.mmdb"
allow_countries DE UNK
}
}
header X-Client-IP "{remote_host}"
handle @geofilter {
error 403
}
}
test.test.de {
import security-header
import geoblock
route {
crowdsec
reverse_proxy authentik_server:9443 {
transport http {
tls_insecure_skip_verify
}
}
}
}
timelordx
(timelordx)
June 29, 2025, 9:05am
6
I don’t think that’s what caused the change in the behaviour.
Caddy in a bridge mode won’t see the real client’s IP, unless there’s another proxy sitting in front of it passing the information to Caddy via X-Forwarded-For.
flg
(Fl)
June 29, 2025, 9:14am
7
True. Caddy is in front - no other proxy involved.
Anyone has an idea why we see this change in behaviour? If caddy does not see the real client IP, why where those listed in the logs?
I am currently thinking of switchting the Geoblocking to my firewall if this tends to be so complicated within Caddy.
Mohammed90
(Mohammed Al Sahaf)
June 29, 2025, 12:31pm
8
You need to disable the userland proxy in Docker so the original remote IP address is retained. Note that there’s been reports of issues when disabling it, but your experience might differ.
opened 03:33PM - 22 Jul 15 UTC
area/networking
The userland proxy was made optional in https://github.com/docker/docker/pull/12… 165 (ported to libnetwork in https://github.com/docker/libnetwork/pull/171),
but still enabled by default because (if I remember correctly) disabling it caused issues on RHEL6 (cf https://github.com/docker/docker/pull/10676).
Now that we will stop supporting RHEL6 with the coming 1.8 release, I think we can change the default to "disabled". We can still keep the `--userland-proxy` option around so that users are able to _enable_ it by setting `--userland-proxy=true`. We can remove the proxy altogether in a future release.
Changing the default might help resolving https://github.com/docker/docker/issues/11185 (possibly others)
Some issues related to disabling the userland-proxy that we should look into;
- [x] https://github.com/moby/moby/issues/21860
- [ ] https://github.com/moby/moby/issues/22741
- [x] https://github.com/moby/moby/issues/28589
- [ ] https://github.com/moby/moby/issues/36214
- [x] https://github.com/moby/moby/issues/37163
- [x] https://github.com/moby/moby/issues/40518
- [x] https://github.com/moby/libnetwork/issues/2423
- [ ] #38784
- [x] https://github.com/moby/moby/issues/44721
flg
(Fl)
June 29, 2025, 12:54pm
9
Thanks for the hint. I now switched to using GeoIP-Shell on the firewall as this is very uncomplicated to setup.
Thanks for your support everyone!