Opened 23 months ago

Last modified 3 months ago

#2350 new enhancement

Option to have set_real_ip_from use the proxied client ip when using proxy protocol.

Reported by: circlingthesun@… Owned by:
Priority: minor Milestone:
Component: documentation Version: 1.19.x
Keywords: Cc:
uname -a: Linux ingress-ingress-nginx-controller-5d794cbf68-qck52 5.10.0-0.bpo.9-amd64 #1 SMP Debian 5.10.70-1~bpo10+1 (2021-10-10) x86_64 Linux
nginx -V: nginx/1.19.10

Description

I'm running nginx on kubernetes in the following configuration:

client -> cloudflare -> load balancer -> nginx ingress -> service

My load balancer runs the proxy protocol and sends traffic to nginx which is on a private network. I'd like trust the X-Forwarded-From header from Cloudflare, but I can't configure that because "set_real_ip_from" refers to the IP of the incoming connection to nginx from my load balancer. When I set "set_real_ip_from" to my private network, which the load balancer is on, ngx_http_realip_module trusts the X-Forwarded-From headers sent to it by my load balancer, which could be coming from anywhere, so it's very easily spoofable.

I'd like the option for "set_real_ip_from" to check the IP of the request forwarded to nginx when using the proxy protocol.

Change History (7)

comment:1 by Maxim Dounin, 23 months ago

As far as I understand your use case, you need to set real IP from two sources:

  • First, from the PROXY protocol, as per set_real_ip_from your.balancer.ip; real_ip_header proxy_protocol;.
  • Second, from the X-Forwarded-For header, as long as the request comes from a trusted address.

As of now, nginx only accepts a single source of addresses: either an HTTP header, or PROXY protocol. A quick workaround would to be use additional proxying to provide appropriate header: something like set_real_ip_from your.balancer.ip; real_ip_header proxy_protocol; with proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; should do the trick. Obviously enough, this is not an optimal solution though.

A better solution might be to teach the realip module to accept variables, so it would be possible to set something like "$http_x_forwarded_for, $proxy_protocol_addr" as an address source.

comment:2 by circlingthesun@…, 23 months ago

That does appear to work! I spent a silly amount of time trying to figure this out. Thanks for the workaround :D

Last edited 23 months ago by circlingthesun@… (previous) (diff)

comment:3 by circlingthesun@…, 23 months ago

On further inspection, the workaround doesn't appear to be working. I might have mistaken the Cloudflare edge server IP for my own.

As far as I understand set_real_ip_from your.balancer.ip; means it trust whatever real_ip_header is set to be the real ip if it comes from my load balancer. So this won't do much because all traffic comes from the load balancer. Then real_ip_header proxy_protocol sets the $remote_addr to the one seen by the load balancer thats running the PROXY protocol. Then proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for appends $remote_addr to the X-Forwarded-For header. So when I'm behind Cloudflare, this would be the edge server IP address, which is what I'm seeing.

Last edited 23 months ago by circlingthesun@… (previous) (diff)

comment:4 by Maxim Dounin, 23 months ago

On the backend server you have to use real_ip_recursive and appropriate set_real_ip_from (with your intermediate proxy IP, your balancer IP, and Cloudflare IPs) to make sure nginx will look through multiple addresses.

comment:5 by Maxim Dounin, 8 months ago

See also #2524.

comment:6 by driskell@…, 8 months ago

I came here from #2524. It would be extremely useful to be able to use variables in real IP module, like "real_ip_variable $fixed_cloudfront_viewer_address". And "real_ip_header" is just a short/faster way to do "real_ip_variable $http_xxxxx".

Then the below example would sort out CloudFront viewer addresses, and similar could sort out any other header or source.

map $http_cloudfront_viewer_address $fixed_cloudfront_viewer_address {
  "~^([0-9a-fA-F:]+):([0-9]+)$" "[$1]:$2";
  default $http_cloudfront_viewer_address;
}

real_ip_from_variable $fixed_cloudfront_viewer_address;

comment:7 by Maxim Dounin, 3 months ago

See also #2584.

Note: See TracTickets for help on using tickets.