How to set real ip header to CF-Connecting-IP for cloudflare proxy. #148

Open
opened 2025-11-13 11:51:24 -06:00 by GiteaMirror · 8 comments
Owner

Originally created by @jbarozi-psl on GitHub (Mar 16, 2025).

I have enabled the cloudflare proxy on the domain side but need a way to set the real ip header to CF-Connection-IP. I do not see a way to set this correctly.

I need this to enable rules on some applications i have behind pangolin.

Originally created by @jbarozi-psl on GitHub (Mar 16, 2025). I have enabled the cloudflare proxy on the domain side but need a way to set the real ip header to CF-Connection-IP. I do not see a way to set this correctly. I need this to enable rules on some applications i have behind pangolin.
GiteaMirror added the good first issueenhancement labels 2025-11-13 11:51:24 -06:00
Author
Owner

@CooperFLe commented on GitHub (Mar 17, 2025):

You should be able to achieve this using traefik plugins. There are numerous for CF proxies. Here is just one example
https://plugins.traefik.io/plugins/62e97498e2bf06d4675b9443/real-ip-from-cloudflare-proxy-tunnel

@CooperFLe commented on GitHub (Mar 17, 2025): You should be able to achieve this using traefik plugins. There are numerous for CF proxies. Here is just one example https://plugins.traefik.io/plugins/62e97498e2bf06d4675b9443/real-ip-from-cloudflare-proxy-tunnel
Author
Owner

@miloschwartz commented on GitHub (Mar 17, 2025):

Hey, we've been aware of the special header that CF sets with the original client's IP. This partly from a lack of understanding on our end, but wouldn't this header be able to be spoofed? Someone could send a different IP as the header to bypass a restriction. Let me know what you think.

@miloschwartz commented on GitHub (Mar 17, 2025): Hey, we've been aware of the special header that CF sets with the original client's IP. This partly from a lack of understanding on our end, but wouldn't this header be able to be spoofed? Someone could send a different IP as the header to bypass a restriction. Let me know what you think.
Author
Owner

@CooperFLe commented on GitHub (Mar 17, 2025):

The client isn't setting the header. It is coming from the middleware, so if the client sets it, the middleware should overwrite it. However, that depends on the implementation of the middleware.

@CooperFLe commented on GitHub (Mar 17, 2025): The client isn't setting the header. It is coming from the middleware, so if the client sets it, the middleware **should** overwrite it. However, that depends on the implementation of the middleware.
Author
Owner

@notmeta commented on GitHub (Apr 8, 2025):

Hey, we've been aware of the special header that CF sets with the original client's IP. This partly from a lack of understanding on our end, but wouldn't this header be able to be spoofed? Someone could send a different IP as the header to bypass a restriction. Let me know what you think.

Cloudflare publishes their IPs which you can use to verify the request came from CF and whether or not to trust the header: https://www.cloudflare.com/ips/

@notmeta commented on GitHub (Apr 8, 2025): > Hey, we've been aware of the special header that CF sets with the original client's IP. This partly from a lack of understanding on our end, but wouldn't this header be able to be spoofed? Someone could send a different IP as the header to bypass a restriction. Let me know what you think. Cloudflare publishes their IPs which you can use to verify the request came from CF and whether or not to trust the header: https://www.cloudflare.com/ips/
Author
Owner

@gitmotion commented on GitHub (May 12, 2025):

You should be able to achieve this using traefik plugins. There are numerous for CF proxies. Here is just one example https://plugins.traefik.io/plugins/62e97498e2bf06d4675b9443/real-ip-from-cloudflare-proxy-tunnel

any luck with this? i set up this plugin and used it on the traefik config but can't seem to get the ip rules working on pangolin.
it's not the biggest deal since im able to block via cf waf rules/etc but would be nice to see if anyone has this passing/working correctly

Edit:

  • not sure if this is accurate but after adding the plugin it looks like my auth provider is able to capture the real ip?
  • however still can't block with pangolin for whatever reason. guessing it has something to do with badger?
  • see below: New York entries coming from real ip VS. Cloudflare IPs below it
    Image
@gitmotion commented on GitHub (May 12, 2025): > You should be able to achieve this using traefik plugins. There are numerous for CF proxies. Here is just one example https://plugins.traefik.io/plugins/62e97498e2bf06d4675b9443/real-ip-from-cloudflare-proxy-tunnel any luck with this? i set up this plugin and used it on the traefik config but can't seem to get the ip rules working on pangolin. it's not the biggest deal since im able to block via cf waf rules/etc but would be nice to see if anyone has this passing/working correctly Edit: - not sure if this is accurate but after adding the plugin it looks like my auth provider is able to capture the real ip? - however still can't block with pangolin for whatever reason. guessing it has something to do with badger? - see below: New York entries coming from real ip VS. Cloudflare IPs below it ![Image](https://github.com/user-attachments/assets/30d52796-640f-4aa2-9659-950342cd597c)
Author
Owner

@gitmotion commented on GitHub (May 13, 2025):

Hey, we've been aware of the special header that CF sets with the original client's IP. This partly from a lack of understanding on our end, but wouldn't this header be able to be spoofed? Someone could send a different IP as the header to bypass a restriction. Let me know what you think.

hey there!

just wanted to add some more findings onto here. i may be wrong but it seems like, if not using cloudflare/not parsing cf headers this is more prone to spoofing?

tldr:
pangolin can't parse cf headers = spoofable with cf workers (current state)
pangolin can parse cf headers = can block ips / cf workers (desired state)

seems like CF-Connecting-IP would be the authoritative truth when proxying through CF

on the contrary however, i also saw that someone can use a cloudflare worker to replace x-real-ip with some caveats

  • https://developers.cloudflare.com/fundamentals/reference/http-headers/#cf-connecting-ip-in-worker-subrequests
  • In same-zone Worker subrequests, the value of CF-Connecting-IP reflects the value of x-real-ip (the client's IP). x-real-ip can be altered by the user in their Worker script. In cross-zone subrequests from one Cloudflare zone to another Cloudflare zone, the CF-Connecting-IP value will be set to the Worker client IP address 2a06:98c0:3600::103 for security reasons. For Worker subrequests destined for a non-Cloudflare customer zone, the CF-Connecting-IP and x-real-ip headers will both reflect the client's IP address, with only the x-real-ip header able to be altered. When no Worker subrequest is triggered, cf-connecting-ip reflects the client's IP address and the x-real-ip header is stripped.

  • interestingly i've seen this ip on my cf waf events 2a06:98c0:3600::103 looking for wordpress exploits, which means they must be using a cf worker. but in either case, being able to pass/parse the CF-Connecting-IP would at least allow CF users to explicitly ban this per resources' ip rules within pangolin
  • Image

where as x-forwarded-for would have the ips appended if already set

  • https://developers.cloudflare.com/fundamentals/reference/http-headers/#x-forwarded-for
  • If, on the other hand, an X-Forwarded-For header was already present in the request to Cloudflare, Cloudflare will append the IP address of the HTTP proxy connecting to Cloudflare to the header. For example, if the original visitor IP address is 203.0.113.1 and a request is proxied through two proxies: proxy A with an IP address of 198.51.100.101 and proxy B with an IP address of 198.51.100.102 before being proxied to Cloudflare, then Cloudflare will send X-Forwarded-For: 203.0.113.1,198.51.100.101,198.51.100.102 to the origin.

i'm sure you looked into this a lot already but in theory it seems like it should be possible to allow users to define a CF_IP_LIST (making it the user's responsibility to keep this up-to-date) or have a setting where CF_PROXY=true

  • then if pangolin/badger comes across one of those ips on the list it knows to check the CF-Connecting-IP instead.

from what it seems like, having the ability to check CF-Connecting-IP is more secure than not being able to

@gitmotion commented on GitHub (May 13, 2025): > Hey, we've been aware of the special header that CF sets with the original client's IP. This partly from a lack of understanding on our end, but wouldn't this header be able to be spoofed? Someone could send a different IP as the header to bypass a restriction. Let me know what you think. hey there! just wanted to add some more findings onto here. i may be wrong but it seems like, if not using cloudflare/not parsing cf headers this is more prone to spoofing? tldr: pangolin **can't** parse cf headers = spoofable with cf workers (current state) pangolin **can** parse cf headers = can block ips / cf workers (desired state) seems like `CF-Connecting-IP` would be the authoritative truth when proxying through CF - https://developers.cloudflare.com/fundamentals/reference/http-headers/#cf-connecting-ip - >CF-Connecting-IP provides the client IP address connecting to Cloudflare to the origin web server. This header will only be sent on the traffic from Cloudflare's edge to your origin web server. on the contrary however, i also saw that someone can use a cloudflare worker to replace x-real-ip with some caveats - https://developers.cloudflare.com/fundamentals/reference/http-headers/#cf-connecting-ip-in-worker-subrequests - > In same-zone Worker subrequests, the value of CF-Connecting-IP reflects the value of x-real-ip (the client's IP). x-real-ip can be altered by the user in their Worker script. In cross-zone subrequests from one Cloudflare zone to another Cloudflare zone, the CF-Connecting-IP value will be set to the Worker client IP address `2a06:98c0:3600::103` for security reasons. For Worker subrequests destined for a non-Cloudflare customer zone, the CF-Connecting-IP and x-real-ip headers will both reflect the client's IP address, with only the x-real-ip header able to be altered. When no Worker subrequest is triggered, cf-connecting-ip reflects the client's IP address and the x-real-ip header is stripped. - interestingly i've seen this ip on my cf waf events `2a06:98c0:3600::103` looking for wordpress exploits, which means they must be using a cf worker. but in either case, being able to pass/parse the `CF-Connecting-IP` would at least allow CF users to explicitly ban this per resources' ip rules within pangolin - ![Image](https://github.com/user-attachments/assets/69678b98-89f1-49d9-821d-429c8757878d) where as `x-forwarded-for` would have the ips appended if already set - https://developers.cloudflare.com/fundamentals/reference/http-headers/#x-forwarded-for - >If, on the other hand, an X-Forwarded-For header was already present in the request to Cloudflare, Cloudflare will append the IP address of the HTTP proxy connecting to Cloudflare to the header. For example, if the original visitor IP address is 203.0.113.1 and a request is proxied through two proxies: proxy A with an IP address of 198.51.100.101 and proxy B with an IP address of 198.51.100.102 before being proxied to Cloudflare, then Cloudflare will send X-Forwarded-For: 203.0.113.1,198.51.100.101,198.51.100.102 to the origin. i'm sure you looked into this a lot already but in theory it seems like it should be possible to allow users to define a `CF_IP_LIST` (making it the user's responsibility to keep this up-to-date) or have a setting where `CF_PROXY`=true - then if pangolin/badger comes across one of those ips on the list it knows to check the `CF-Connecting-IP` instead. from what it seems like, having the ability to check `CF-Connecting-IP` is more secure than not being able to
Author
Owner

@overclockit commented on GitHub (May 16, 2025):

As I mentioned in GitHub Issue #619, Badger's current implementation handles IP validation relying solely on &req.RemoteAddr to determine client IP and whether an IP matches a rule, which when using a proxy, whether Cloudflare/Orange Cloud or other will always return the address of the proxy or service creating/forwarding the HTTP request, like a CF worker.

I have this working in my setup by using a locally edited version of Badger but any permanent fix should come with validation that trustedIPs is set and almost as importantly not wide open / set to 0.0.0.0/0. So long as trustedIPs is already set within Traefik using Cloudflare CIDR's, Badger doesn't need to revalidate the header as it is assumed trusted by Traefik's own rules.

@overclockit commented on GitHub (May 16, 2025): As I mentioned in GitHub Issue [#619](https://github.com/fosrl/pangolin/issues/619#issuecomment-2881322127), Badger's current implementation handles IP validation relying solely on &req.RemoteAddr to determine client IP and whether an IP matches a rule, which when using a proxy, whether Cloudflare/Orange Cloud or other will always return the address of the proxy or service creating/forwarding the HTTP request, like a CF worker. I have this working in my setup by using a locally edited version of Badger but any permanent fix should come with validation that trustedIPs is set and almost as importantly not wide open / set to 0.0.0.0/0. So long as trustedIPs is already set within Traefik using Cloudflare CIDR's, Badger doesn't need to revalidate the header as it is assumed trusted by Traefik's own rules.
Author
Owner

@miloschwartz commented on GitHub (May 19, 2025):

@OverclockIT Understood this makes sense. If anyone has the capacity to open a PR to begin work on this that would be a huge help, but we want to get to this.

@miloschwartz commented on GitHub (May 19, 2025): @OverclockIT Understood this makes sense. If anyone has the capacity to open a PR to begin work on this that would be a huge help, but we want to get to this.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/pangolin#148