Appending to the XFF header does not work as expected

Hi, I would like HAProxy to append to the X-Forwarded-For header. As HAProxy preferers to add duplicate headers instead of appending the existing list this does not seem to be so straight forward. I found a configuration line form HAProxy issue tracker on github that should append the value to the comma-delimited X-Forwarded-For downstream header but I don’t get the expected results.

My downstream X-Forwarded-For contains 3 IP addresses already: presumed client ip, edge reverse proxy ip, ingress controller ip. I also want HAProxy do add the auth gateway ip which is between ingress controller and HAProxy. Upstream servers are different application and web servers. ingress controller ip is the last entry of %[hdr(x-forwarded-for)] and auth gateway ip is in %[src]

The line (from HAProxy issute tracker on github) is

http-request replace-value x-forwarded-for ^ "%[hdr(x-forwarded-for)], %[src]"

The result I get is: “X-Forwarded-For: ingress controller ip, auth gateway ip, ingress controller ip, auth gateway ip, ingress controller ip, auth gateway ip” -basically it just replaces all three unique IP addresses with the two values from replace-fmt. What is even more confusing is that %[hdr(x-forwarded-for)] onl contains the most right i.e latest IP and others are discarded. Looks like improper parsing of comma-delimited lists.

I am using HAProxy version 2.3.5

Yes indeed hdr appears to be the wrong tool for the job. It is parsing comma as delimiter, so it returns 3 times in this case.

Replace it with fhdr, that should do the job.

http-request replace-value x-forwarded-for ^ "%[fhdr(x-forwarded-for)], %[src]"

Thanks @lukastribus! Your answer was almost the correct one. I ended up with

http-request replace-header x-forwarded-for ^ "%[req.fhdr(x-forwarded-for)], %[src]"

replace-header instead of replace-value because replace value would give me:

X-Forwarded-For: presumed client ip, ingress controller ip, auth gateway ip,  HAProxy ip,presumed client ip, ingress controller ip, auth gateway ip,  HAProxy ip,presumed client ip, ingress controller ip, auth gateway ip,  HAProxy ip,

and req.fhdr because HAProxy did not recognize fhdr as a fetch method.

1 Like

Thanks, I noted this configuration in the issue tracker too.

Be careful with this. By appending a value to an existing header instead of adding a new header, you’re giving the client all the keys to manipulate its contents and format, allowing it to be unparsable by the last server in the chain, effectively opening a security issue. While originally the extra header used to be a technical limitation, it’s now a reliability feature. If your server is having difficulties parsing header lists, it should be fixed instead of hacking on a header without prior checking that its contents will not cause trouble.

It depends on your trust chain. For us the first ip to the XFF list added by edge reverse proxy, we are not accepting the XFF header from outside. If the client sends us a list or even single value this is deleted and XFF header is create by edge reverse proxy.

OK that’s fine, it’s the safe way to use it (but rarely the one seen in field :-))