Cloudflare + Remote IP

1. The problem I’m having:

my issue is that
remote_ip = 127.0.0.1 , so geo location allow not working

I was tried to change :
client_ip_headers CF-Connecting-IP X-Forwarded-For
to
client_ip_headers Cf-Connecting-Ip X-Forwarded-For

also same issue

Access log by getting request ip is working good

My caddy implemenation :

Linux Server running cloudflared tunnel —> Docker —> Caddy Container

2. Error messages and/or full log output:

Nothing , everything is working good except the remote_ip issue

3. Caddy version:

caddy:2.8

4. How I installed and ran Caddy:

Docker container

FROM caddy:2.8-builder-alpine AS builder

RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare \
    --with github.com/abiosoft/caddy-hmac \
    --with github.com/caddy-dns/dnspod \
    --with github.com/caddy-dns/gandi \
    --with github.com/caddy-dns/route53 \
    --with github.com/caddy-dns/alidns \
    --with github.com/caddy-dns/azure \
    --with github.com/caddy-dns/digitalocean \
    --with github.com/caddy-dns/duckdns \
    --with github.com/caddy-dns/hetzner \
    --with github.com/caddy-dns/openstack-designate \
    --with github.com/caddy-dns/vultr \
    --with github.com/lolPants/caddy-requestid \
    --with github.com/mholt/caddy-webdav \
    --with github.com/abiosoft/caddy-json-parse \
    --with github.com/hslatman/caddy-crowdsec-bouncer \
    --with github.com/porech/caddy-maxmind-geolocation \
    --with github.com/WeidiDeng/caddy-cloudflare-ip \
    --with github.com/fvbommel/caddy-combine-ip-ranges \
    --with github.com/corazawaf/coraza-caddy/v2 \
    --with github.com/caddyserver/transform-encoder

FROM caddy:2.8-alpine

COPY --from=builder /usr/bin/caddy /usr/bin/caddy
RUN mkdir -p /usr/share/GeoIP
COPY GeoData /usr/share/GeoIP

a. System environment:

Debian 12
Docker version 26.1.2, build 211e74b
Caddy 2.8

b. Command:

    command: ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--watch"]

c. Service/unit/compose file:

Docker Compose file

version: '2.4'
services:
  caddy:
    image: chunky/caddy:latest
    container_name: caddy
    restart: always
    network_mode: "host"
    volumes:
      - type: "bind"
        source: "./Caddyfile"
        target: "/etc/caddy/Caddyfile"
      - "./data:/data/caddy"
      - "./logs:/var/log/caddy"
    command: ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--watch"]

d. My complete Caddy config:

{

        # runtime logs
        log {
                format json # set runtime log format to json mode
        }
        # server options
        servers {
              trusted_proxies cloudflare
              client_ip_headers CF-Connecting-IP X-Forwarded-For

        }
}

domain.net {


     @geofilter {
                maxmind_geolocation {
                        db_path "/usr/share/GeoIP/GeoLite2-Country.mmdb"
                        allow_countries SA
                }
                                   }

    respond "Hello, Chunky!! {client_ip} "

    log {
        output file /var/log/caddy/access.log
        format transform `{request>host}:443 {request>headers>X-Forwarded-For>[0]:request>headers>Cf-Connecting-Ip>[0]:request>remote_ip} - {request>user_id} [{ts}] "{request>method} {request>uri} {request>proto}" {status} {size} "{requ>
        time_format "02/Jan/2006:15:04:05 -0700"
                  }
    }


}

:thinking: I don’t think you’re using all those plugins. Only build Caddy with the plugins you need.

If the maxmind_geolocation plugin is reading from remote IP, then it will need to be updated to have a feature to read from client_ip instead. Reach out to the plugin’s author for that.

1 Like

If you noticed, I’m using respond to show client_ip ,it’s show 127.0.0.1

We need to see your logs. Enable the debug global option and show what you get.

I have the same question.

It happens after cloudflare tunnel usage. I am running caddy and cloudflared in docker. I closed my ports and only serve over tunnel. When I check my access.log, I see all the ‘client_ip’ fields refers to local IP. I can see real user IP in both Cf-Connecting-Ip and X-Forwarded-For but ‘client_id’ does not use it.

I also have server->trusted proxies configuration. Is it a case like, because cloudflared running locally and forward traffic to caddy container, caddy do not apply ‘trusted_proxies’ rules and do not parse IP from X-Forwarded-For?

{“remote_ip”:“172.19.0.2”,“remote_port”:“53502”,“client_ip”:“172.19.0.2”,“proto”:“HTTP/2.0”,“method”:“GET”,“host”:“example.com”,“uri”:“/”,“headers”:{“Cdn-Loop”:[“cloudflare”],“Accept-Encoding”:[“gzip, br”],“Priority”:[“u=1, i”],“Cf-Ipcountry”:[“CA”],“Cf-Connecting-Ip”:[“37.120.237.162”],“Sec-Ch-Ua-Mobile”:[“?0”],“Authorization”:,“Sec-Fetch-Site”:[“same-origin”],“User-Agent”:[“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36”],“Content-Type”:[“application/json”],“Sec-Ch-Ua-Platform”:[“"Windows"”],“Sec-Fetch-Mode”:[“cors”],“X-Forwarded-For”:[“37.120.237.162”],“Sec-Ch-Ua”:[“"Chromium";v="124", "Brave";v="124", "Not-A.Brand";v="99"”],“Accept”:[“/”],“Sec-Gpc”:[“1”],“Sec-Fetch-Dest”:[“empty”],“Cf-Warp-Tag-Id”:[“6be7c6b6-2acb-4ff1-a7d4-5a1c02d75738”],“Accept-Language”:[“en-US,en;q=0.7”],“Cf-Visitor”:[“{"scheme":"https"}”],“X-Forwarded-Proto”:[“https”],“Cf-Ray”:[“884b1b620ac85443-YYZ”]},“tls”:{“resumed”:false,“version”:772,“cipher_suite”:4865,“proto”:“h2”,“server_name”:“example.com”}},“bytes_read”:0,“user_id”:“”,“duration”:0.187907839,“size”:0,“status”:401

Should I stop using ‘servers { trusted_proxies }’ if I am serving over tunnel? And manually pass the IP using header_up? Or is it something needs to be fixed by caddy?

Ah, so Caddy sees the request as coming from your other Docker container (cloudflared), not from Cloudflare themselves. trusted_proxies looks at remote_ip to determine trust. In your case, you should probably use trusted_proxies static private_ranges so that it trusts requests coming from other containers, instead of trying to trust Cloudflare’s IP ranges.

2 Likes

For me , after changing

trusted_proxies cloudflare

to

trusted_proxies static private_ranges

client ip works on respond . but geo plugin i think still see 127.0.0.1

debug log :

caddy  | {"level":"info","ts":1715989699.9459617,"msg":"using config from file","file":"/etc/caddy/Caddyfile"}
caddy  | {"level":"info","ts":1715989699.9465668,"msg":"adapted config to JSON","adapter":"caddyfile"}
caddy  | {"level":"warn","ts":1715989699.946575,"msg":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"/etc/caddy/Caddyfile","line":9}
caddy  | {"level":"info","ts":1715989699.9471266,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
caddy  | {"level":"info","ts":1715989699.947189,"logger":"http.auto_https","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
caddy  | {"level":"info","ts":1715989699.9471982,"logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
caddy  | {"level":"info","ts":1715989699.947309,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc000141480"}
caddy  | {"level":"debug","ts":1715989699.9472277,"logger":"http.auto_https","msg":"adjusted config","tls":{"automation":{"policies":[{}]}},"http":{"servers":{"remaining_auto_https_redirects":{"listen":[":80"],"routes":[{},{}],"logs":{"logger_names":{"domain.net":["log0"]}}},"srv0":{"listen":[":443"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"body":"Hello, Chunky!! Funkky ?? Trickky? {http.vars.client_ip} ","handler":"static_response"}]}]}],"terminal":true}],"tls_connection_policies":[{}],"automatic_https":{},"trusted_proxies":{"ranges":["192.168.0.0/16","172.16.0.0/12","10.0.0.0/8","127.0.0.1/8","fd00::/8","::1"],"source":"static"},"client_ip_headers":["Cf-Connecting-Ip","X-Forwarded-For"],"logs":{"logger_names":{"domain.net":["log0"]}}}}}}
caddy  | {"level":"info","ts":1715989699.9474518,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
caddy  | {"level":"info","ts":1715989699.9475033,"msg":"failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 7168 kiB, got: 416 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details."}
caddy  | {"level":"debug","ts":1715989699.9476275,"logger":"http","msg":"starting server loop","address":"[::]:443","tls":true,"http3":true}
caddy  | {"level":"info","ts":1715989699.947638,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
caddy  | {"level":"debug","ts":1715989699.9476647,"logger":"http","msg":"starting server loop","address":"[::]:80","tls":false,"http3":false}
caddy  | {"level":"info","ts":1715989699.9476724,"logger":"http.log","msg":"server running","name":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]}
caddy  | {"level":"info","ts":1715989699.9476767,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["domain.net"]}
caddy  | {"level":"debug","ts":1715989699.9479373,"logger":"tls.cache","msg":"added certificate to cache","subjects":["domain.net"],"expiration":1723484553,"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"2788288cb4fbd4aec4b31f953aab1101065065f9eee8a709df9665c06a6404c3","cache_size":1,"cache_capacity":10000}
caddy  | {"level":"debug","ts":1715989699.947957,"logger":"events","msg":"event","name":"cached_managed_cert","id":"1b11bc75-415c-4d88-b1ab-52b2af99f9ee","origin":"tls","data":{"sans":["domain.net"]}}
caddy  | {"level":"info","ts":1715989699.9481685,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
caddy  | {"level":"info","ts":1715989699.948176,"msg":"serving initial configuration"}
caddy  | {"level":"info","ts":1715989699.9484625,"logger":"watcher","msg":"watching config file for changes","config_file":"/etc/caddy/Caddyfile"}
caddy  | {"level":"info","ts":1715989699.951296,"logger":"tls","msg":"storage cleaning happened too recently; skipping for now","storage":"FileStorage:/data/caddy","instance":"3dde9029-c18b-4f37-8ba4-f994841fc090","try_again":1716076099.9512904,"try_again_in":86399.999998045}
caddy  | {"level":"info","ts":1715989699.9513798,"logger":"tls","msg":"finished cleaning storage units"}
caddy  | {"level":"debug","ts":1715989710.4759433,"logger":"events","msg":"event","name":"tls_get_certificate","id":"929e7c93-367e-46cb-9b00-3177f679189f","origin":"tls","data":{"client_hello":{"CipherSuites":[49195,49199,49196,49200,52393,52392,49161,49171,49162,49172,156,157,47,53,49170,10,4865,4866,4867],"ServerName":"domain.net","SupportedCurves":[29,23,24,25],"SupportedPoints":"AA==","SignatureSchemes":[2052,1027,2055,2053,2054,1025,1281,1537,1283,1539,513,515],"SupportedProtos":null,"SupportedVersions":[772,771],"RemoteAddr":{"IP":"127.0.0.1","Port":59156,"Zone":""},"LocalAddr":{"IP":"127.0.0.1","Port":443,"Zone":""}}}}
caddy  | {"level":"debug","ts":1715989710.4759743,"logger":"tls.handshake","msg":"choosing certificate","identifier":"domain.net","num_choices":1}
caddy  | {"level":"debug","ts":1715989710.4759922,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"domain.net","subjects":["domain.net"],"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"2788288cb4fbd4aec4b31f953aab1101065065f9eee8a709df9665c06a6404c3"}
caddy  | {"level":"debug","ts":1715989710.4760003,"logger":"tls.handshake","msg":"matched certificate in cache","remote_ip":"127.0.0.1","remote_port":"59156","subjects":["domain.net"],"managed":true,"expiration":1723484553,"hash":"2788288cb4fbd4aec4b31f953aab1101065065f9eee8a709df9665c06a6404c3"}

Yes, because that plugin reads from remote_ip only. You’ll need to get the author to add an option to read from the parsed client IP. They can reach out to us for help if they need it.

1 Like