I’m trying to get the real IP from cloudflare clients using the CF-Connecting-IP header. I’ve done this successfully on nginx using the real IP module.
The way I attempted to do it with haproxy is setting a variable, and logging based on it. For some reason why I do this half of my logs are empty.
This is my configuration:
# Check if CF header set
acl is_real_ip hdr_ip(CF-Connecting-IP) -m len 0
# Header set, set txn.real_ip to CF ip
http-request set-var(txn.real_ip) hdr_ip(CF-Connecting-IP) if ! is_real_ip
# Set txn.real_ip to src ip
http-request set-var(txn.real_ip) src if is_real_ip
log-format "%[var(txn.real_ip)] %cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"
I would think based on this they would either get the source IP or the cloudflare IP, but sometimes they are empty, and %[var(txn.real_ip)] is replaced with '-'.
I’m also not sure if there’s an easier way to do it than this.
Share the entire configuration and the output of haproxy -vv.
I’d guess that some request come in directly, without going through Cloudflare.
That ACL is probably not doing what you indent it to do and the configuration is also a huge security risk. Everyone connecting to your server directly could just send an CF-Connecting-IP header and therefor you’d log a attacker controller IP, not the real IP of the attacker.
You need to match the real IP with a cloudflare whitelist, that’s how you know it comes from Cloudflare:
First of all you are not accessing the header, but transforming the header into an IP address (hdr_ip). So an empty header most likely gets transformed into 0.0.0.0.
Second, len is supposed to be applied to a string. What happens if you apply it to an IP address (not a string of an IP address) is undefined.
And then of course your logic would actually be inverted:
you are saying the ACL is supposed to set is_real_ip to true, if the header is NOT SET
but you are using the ACL - as it’s name implies - like it would be true if the header IS SET