-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compute a real X-Forwarded-For
header
#1489
Conversation
@maxlaverse the current code returns the correct IP address when the ingress controller is running behind a L7 load balancer. |
Please use the image in #1487 |
Hi @aledbf ! I've not used this image but I built one based on your commit 48a58bb, which was the most recent commit a few hours ago. The PR is not about the content of In its current form, the Ingress currently erases that header and puts the computed real-ip in it. Even if it should work with most of the implementation that parse this header to get the true client ip out of it, it erases some information. You expect from a L7 load-balancer to append the tcp client IP to the With the current implementation of the Ingress, I can't know anymore through which load-balancers a requests went. But I as I said, in most cases you don't care actually, through which L7 component your request went and the current behavior is just fine. This is just a proposition PR to make the Ingress behave as any other L7 load-balancer/reverse-proxy. |
@maxlaverse the issue is that you can fake the header. That is the reason why I added a copy of the original header. I'm not sure I can provide a one size fits all for this. Maybe we can add a new configuration in the configmap? |
1b5af45
to
65ba0c1
Compare
The last commit makes this behavior configurable. |
rootfs/etc/nginx/template/nginx.tmpl
Outdated
# replaces the remote_addr to soon | ||
map $http_x_forwarded_for $full_x_forwarded_for { | ||
default "$http_x_forwarded_for, $realip_remote_addr"; | ||
'' "$realip_remote_addr"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
realip_remote_addr does not contains the client IP address. Please use $the_real_ip
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's on purpose. Here I want to append the ip of the actual tcp client connected to the Ingress.
The goal of this PR is to have the X-Forwarded-For
header arriving on upstreams has if it had been through any classic L7 load-balancer, and the last part of it should be the last proxy the request went through.
Maybe my explanations are not clear enough, let me add an example.
Client (1.1.1.1) => L7 Load-balancer A (2.2.2.2) => L7 Ingress B (3.3.3.3) => Pods
The Load-balancer A appends its IP to the X-Forwarded-For
header of the request, or create the header if it doesn't exist (which should be the case).
If 3.3.3.3
was another L7 load-balancer/reverse proxy, it would also append 2.2.2.2
to the value of X-Forwarded-For
and the client would receive X-Forwarded-For: 1.1.1.1, 2.2.2.2
.
But with the current Ingress, the Pod receives the request with the header X-Forwarded-For: 1.1.1.1
if proxy-real-ip-cidr: 2.2.2.2
. We loose the information that the request has been through 2.2.2.2
, expect if we look in X-Original-X-Forwarded-For
but this is not standard.
With the PR, no matter the value of proxy-real-ip-cidr
, the Pod would receive the request with the header X-Forwarded-For: 1.1.1.1, 2.2.2.2
. The implementation of the pod could check the header, compare 2.2.2.2
to a list of trusted proxy and because it matches, set the real-ip to 1.1.1.1
, or rely of X-Real-IP
directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you know, I would vote for having this the default behavior and X-Original-Forwarded-For
removed :)
But I understand it could break some older Ingress Nginx setup :/
@abh ping. This is the expected behavior you mentioned with X-Forwarded-For? |
Yeah, it looks right. nginx should just be adding the "upstream" IP address to the list of X-Forwarded-For IPs. If using the proxy protocol (and proxy-real-ip-cidr matches?), add the IP from that rather than the "tcp IP". Downstream clients have to implement their own whitelist and decide how many "hops" in the list to trust. That's only possible if it can trust each hop to add to the list as described above. For the X-Real-IP header, maybe that'd be the right place to just put the last upstream IP (or proxy IP). |
Agreed
I never used the proxy protocol but I thought even with proxy protocol activated, you would still want the tcp ip in
|
The thing with the proxy protocol is that it still might not be the real client IP, it could just be "one more proxy hop". The proxy that's using PROXY protocol likely is doing so because it's not able to add HTTP headers, for example if it's a TCP proxy passing through TLS. client (1.1.1.1) -> proxy1 (2.2.2.2, adds 1.1.1.1 to XFF) -> tcp proxy2 (10.0.0.10) --[proxy protocol says 2.2.2.2]--> kube ingress (10.2.0.1) --> pod The pod gets a connection from 10.2.0.1 (which it trusts because it's internal in kube or maybe even it's looked up that this is indeed an IP from the nginx ingress). In X-Forwarded-For you want "1.1.1.1, 2.2.2.2". The pod can now decide if it just wants to use 2.2.2.2 as the client IP, or if it trusts 2.2.2.2 and then uses 1.1.1.1. Alternatively (as you maybe suggested) the last hop could add both the IP from PROXY and the actual ip making it "1.1.1.1, 2.2.2.2, 10.0.0.10". |
Sorry if I repeat what you already said, the proxy protocol is new to me. If proxy2 talks to the nginx ingress using the proxy protocol, I would expect "1.1.1.1, 2.2.2.2" in the xff header arriving on the pod. "1.1.1.1" because it's the value of the xff header nginx gets. 2.2.2.2 because it's the remote_addr according to the proxy protocol as received by nginx (and not the "tcp ip") I wouldn't add 10.0.0.10 to the xff in that case. Expecting "1.1.1.1, 2.2.2.2" sounds right. As you said, it's to the pod to decide if it trusts 2.2.2.2.
Now I get that. If we receive a request on the Nginx with the proxy protocol, we should append the client address as sent in the proxy request (if we trust the proxy), not the tcp addr. |
On another topic, does the If you activate the proxy_protocol on the Ingress, if will add the directive
Wouldn't the remote_addr on Nginx as modified by the real_ip module become 2.2.2.2 instead of 1.1.1.1, if 2.2.2.2 is a trusted proxy ? |
rootfs/etc/nginx/template/nginx.tmpl
Outdated
@@ -195,6 +195,15 @@ http { | |||
'' $host; | |||
} | |||
|
|||
{{ if $cfg.ComputeFullForwardedFor }} | |||
# We can't use $proxy_add_x_forwarded_for because the realip module | |||
# replaces the remote_addr to soon |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
too soon
Coverage increased (+0.01%) to 33.505% when pulling 55c2097a9492a2ac22f363f34ccd34502685573e on maxlaverse:fix_x_forwarded_for into 1f269d4 on kubernetes:master. |
Would you have time to look at my last comments this week @abh ? (or anyone else) |
55c2097
to
bfe2030
Compare
To backup this proposition: https://tools.ietf.org/html/rfc7239, section 5.2 |
/lgtm |
@maxlaverse: nice work, looks good to me. Thank you. |
Thanks for merging. @abh @aledbf, I still have this open question when Don't hesitate to ping me if this needs to be changed. I would push a new PR. |
I think when proxy protocol is used it should use the X-Forwarded-For that’s in the request plus the IP from the proxy protocol.
A variation would be adding the IP from the proxy protocol and the IP from the tcp connection (in that order), but I think using the proxy protocol implies that you trust the proxy and don’t care about its IP.
|
Hi !
It's great that the real-ip module has been built in the Ingress. It simplifies the work of a lot of applications by extracting the real client ip automatically for them, avoid the hassle of implementing this is a lot of different languages.
However, the
X-Forwarded-For
header is supposed to contain all the L7 LB or Reverse-proxies that have been crossed, from the client to the application. Of course this information could still be computed by appendingremote_addr
to theX-Original-Forwarded-For
header on the server side, but it sounds a bit weird.I would expect the Ingress to behave as any classic reverse-proxy installation, but maybe my "classic" picture is wrong.
On the other hand, if you are only interested in having the real client IP and you extract it from the
x-forwarded-for
header, since you already trust the Ingress it should not be an issue to loose the addresses of other L7 reverse-proxy that have been traversed.