Cloudflare + Kubernetes: How can I preserve the real client IP behind multiple proxies? #208603
🏷️ Discussion TypeQuestion 💬 Feature/Topic AreaCode Search and Navigation BodyI'm running a web application behind Cloudflare and Traefik on K3s. Because the request passes through Cloudflare before reaching Traefik, the source IP visible at the Kubernetes ingress isn't necessarily the original client IP. I'm trying to understand the safest way to preserve and use the real client IP for application logging and security-related decisions. What is the recommended approach for this architecture? Specifically:
I want to make sure the configuration doesn't allow an arbitrary client to spoof its IP by sending its own forwarding headers. |
Replies: 2 comments 1 reply
|
The important part here is deciding which proxy is trusted to provide the client IP information. With Cloudflare in front of Traefik, you generally shouldn't simply trust arbitrary X-Forwarded-For values from the internet. The application or ingress should only trust forwarded client information when it came from a known trusted proxy. Cloudflare provides CF-Connecting-IP for the original client address. Traefik can also work with forwarded headers when its trusted proxy configuration is restricted to the proxy IP ranges you actually trust. So the general model is: Client -> Cloudflare -> Traefik -> Application Cloudflare is the trusted proxy, and Traefik is configured to trust forwarded information from Cloudflare rather than from arbitrary sources. I would avoid enabling Proxy Protocol unless the connection immediately upstream of Traefik is actually configured to send Proxy Protocol. It's not a generic replacement for forwarded HTTP headers, and enabling it when the upstream isn't using it can break connections. For verification, log the values at the ingress/application boundary and test from a controlled external client. It's useful to compare the observed address with the address reported by Cloudflare and also test what happens when you manually send spoofed X-Forwarded-For headers. The security principle is more important than the specific header: never treat a client-controlled forwarding header as authoritative unless the request came through a proxy that you explicitly trust to set or sanitize that header. |
|
Establish a clear trust boundary. The application should only trust client-IP headers, when they were added/rewritten by a proxy you explicitly trust. A typical flow is: Client -> Cloudflare -> Traefik -> Application
Conceptually: entryPoints: The exact configuration depends on whether you're using Traefik's static configuration, Helm values, or Kubernetes CRDs, but there stays the thumb rule : trust Cloudflare's networks, not the public internet. Cloudflare publishes its current IPv4 and IPv6 ranges, so those should be sourced from Cloudflare rather than copied from an old example.
X-Forwarded-For: 1.2.3.4 If your ingress blindly trusts that header from an untrusted connection, the application may believe the attacker is 1.2.3.4.
For example: Client → Load Balancer → Traefik A load balancer can use Proxy Protocol to communicate the original source address to Traefik without relying only on HTTP headers. If you're using Cloudflare's HTTP proxy/CDN path, the relevant Cloudflare headers and a properly defined trusted-proxy boundary are usually the simpler model. Also, be careful not to enable Proxy Protocol on an entrypoint unless the thing connecting to that entrypoint is actually speaking Proxy Protocol. Otherwise you can break normal traffic.
For example, temporarily log:
at Traefik and/or the application. Then make a request from a known client IP and verify that: Client -> Cloudflare: CF-Connecting-IP = -> Traefik: extracts client IP only from trusted Cloudflare traffic -> Application: sees the expected client IP Then perform a second test, by sending a request containing an intentionally fake X-Forwarded-For value through an untrusted path. The application should not treat that arbitrary value as the client's address. So in a nutshell, Trust Cloudflare, not arbitrary forwarding headers. Configure Traefik's trusted proxy ranges to Cloudflare's current IP ranges, use the appropriate Cloudflare client-IP header, and verify the complete proxy chain with logs/tests. |
The important part here is deciding which proxy is trusted to provide the client IP information.
With Cloudflare in front of Traefik, you generally shouldn't simply trust arbitrary X-Forwarded-For values from the internet. The application or ingress should only trust forwarded client information when it came from a known trusted proxy.
Cloudflare provides CF-Connecting-IP for the original client address. Traefik can also work with forwarded headers when its trusted proxy configuration is restricted to the proxy IP ranges you actually trust.
So the general model is:
Client -> Cloudflare -> Traefik -> Application
Cloudflare is the trusted proxy, and Traefik is configured to trust forwarded …