I'm getting different x_forwarded_for in my Gorouter access logs depending on what browser/cli-tool I use.


Simon Johansson <simon@...>
 

Hiya!

Im looking into an issue where the x_forwarded_for have different values depending on what you are using to hit it with.
With curl, w3m, lynx x_forwarded_for gets set to "sourceIP, a-gateway-ip"
whereas with Firefox, Chrome, Opera, wget x_forwarded_for is simply set to "sourceIP"

This is causing confusion but most of all it makes me tear my hair out as I cannot figure out what is going on, from what I can see the issue is not in Gorouter directly but in the stdlib of Golang.

I have made two patches to figure out what is going on

In the gorouter

--- a/proxy/proxy.go
+++ b/proxy/proxy.go
@@ -2,18 +2,18 @@ package proxy

import (
"errors"
+ "fmt"
"io"
)

const (
@@ -117,6 +117,8 @@ func (p *proxy) lookup(request *http.Request) *route.Pool {
}

func (p *proxy) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
+ fmt.Println("In proxy.ServeHTTP")
+ fmt.Println("Request: ", request)
startedAt := time.Now()

accessLog := access_log.AccessLogRecord{
@@ -207,7 +209,9 @@ func (p *proxy) ServeHTTP(responseWriter http.ResponseWriter, request *http.Requ
},
}

+ fmt.Println("X-Forwarded-For before newReverseProxy.ServeHTTP: ", request.Header.Get("X-Forwarded-For"))
p.newReverseProxy(roundTripper, request).ServeHTTP(proxyWriter, request)
+ fmt.Println("X-Forwarded-For after newReverseProxy.ServeHTTP: ", request.Header.Get("X-Forwarded-For"))

accessLog.FinishedAt = time.Now()
accessLog.BodyBytesSent = proxyWriter.Size()


And in golang/src/net/http/httputil/reverseproxy.go
--- a/src/net/http/httputil/reverseproxy.go
+++ b/src/net/http/httputil/reverseproxy.go
@@ -7,6 +7,7 @@
package httputil

import (
+ "fmt"
"io"
"log"
"net"
@@ -101,6 +102,7 @@ var hopHeaders = []string{
}

func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
+ fmt.Println("In net/http/httputil/reverseproxy.go: ServeHTTP")
transport := p.Transport
if transport == nil {
transport = http.DefaultTransport
@@ -132,6 +134,7 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
}

+ fmt.Println("X-Forwarded-For in req before 'If we aren't the first proxy retain prior': ", req.Header.Get("X-Forwarded-For"))
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
// If we aren't the first proxy retain prior
// X-Forwarded-For information as a comma+space
@@ -140,7 +143,9 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
clientIP = strings.Join(prior, ", ") + ", " + clientIP
}
outreq.Header.Set("X-Forwarded-For", clientIP)
+ fmt.Println("X-Forwarded-For in outreq: ", outreq.Header.Get("X-Forwarded-For"))
}
+ fmt.Println("X-Forwarded-For in req after 'If we aren't the first proxy retain prior': ", req.Header.Get("X-Forwarded-For"))

res, err := transport.RoundTrip(outreq)
if err != nil {
@@ -158,6 +163,7 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {

rw.WriteHeader(res.StatusCode)
p.copyResponse(rw, res.Body)
+ fmt.Println("Done in net/http/httputil/reverseproxy.go: ServeHTTP")
}


So basically debug printing.

This is the difference I see (first example with 2 ips in the headers, second example with 1 ip in the headers), both requests from the same machine to the same Gorouter.

Logs from gorouter
In proxy.ServeHTTP
Request: &{GET / HTTP/1.1 1 1 map[Accept:[*/*] Forwarded:[for=172.21.27.221; proto=http] X-Forwarded-Proto:[http] X-Forwarded-For:[172.21.27.221] True_client_ip:[172.21.27.221] X-Cf-Requestid:[874ed33a-c361-4946-74c3-c18b693ade7e] User-Agent:[curl/7.43.0]] 0xbb2730 0 [] false cf-env.test.cf.springer-sbm.com map[] map[] <nil> map[] 10.230.31.8:43597 / <nil>}
X-Forwarded-For before newReverseProxy.ServeHTTP: 172.21.27.221
In net/http/httputil/reverseproxy.go: ServeHTTP
X-Forwarded-For in req before 'If we aren't the first proxy retain prior': 172.21.27.221
X-Forwarded-For in outreq: 172.21.27.221, 10.230.31.8
X-Forwarded-For in req after 'If we aren't the first proxy retain prior': 172.21.27.221, 10.230.31.8
Done in net/http/httputil/reverseproxy.go: ServeHTTP
X-Forwarded-For after newReverseProxy.ServeHTTP: 172.21.27.221, 10.230.31.8

$ cf logs cf-env
2015-08-19T15:11:36.22+0200 [RTR/0] OUT cf-env.test.cf.springer-sbm.com - [19/08/2015:13:11:36 +0000] "GET / HTTP/1.1" 200 0 4155 "-" "curl/7.43.0" 10.230.31.8:43597 x_forwarded_for:"172.21.27.221, 10.230.31.8" vcap_request_id:c2ce64d5-5951-46fb-7b0a-9ea233c34823 response_time:0.011068200 app_id:9011c83a-9407-4e0a-ae91-0c66ff3d6e92


Logs from gorouter
In proxy.ServeHTTP
Request: &{GET / HTTP/1.1 1 1 map[True_client_ip:[172.21.27.221] X-Cf-Requestid:[d7629a05-cbae-4f13-63d2-43acf5bfe8c9] User-Agent:[Mozilla/5.0 (X11; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0] Accept-Language:[en-US,en;q=0.5] Accept-Encoding:[gzip, deflate] Forwarded:[for=172.21.27.221; proto=http] X-Forwarded-Proto:[http] X-Forwarded-For:[172.21.27.221] Accept:[text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8] Cookie:[wt3_eid=%3B846182373400841%7C2141346926300579527%3B532695141032829%7C2142289007300951926%3B929408468507536%7C2142323421600457694%3B987963572816400%7C2142323579400274413%3B595753140200997%7C2142364572200667778%3B754741632944378%7C2142539934000605010] Connection:[keep-alive]] 0xbb2730 0 [] false cf-env.test.cf.springer-sbm.com map[] map[] <nil> map[] 10.230.31.8:43601 / <nil>}
X-Forwarded-For before newReverseProxy.ServeHTTP: 172.21.27.221
In net/http/httputil/reverseproxy.go: ServeHTTP
X-Forwarded-For in req before 'If we aren't the first proxy retain prior': 172.21.27.221
X-Forwarded-For in outreq: 172.21.27.221, 10.230.31.8
X-Forwarded-For in req after 'If we aren't the first proxy retain prior': 172.21.27.221
Done in net/http/httputil/reverseproxy.go: ServeHTTP
X-Forwarded-For after newReverseProxy.ServeHTTP: 172.21.27.221

$ cf logs cf-env
2015-08-19T15:12:16.28+0200 [RTR/0] OUT cf-env.test.cf.springer-sbm.com - [19/08/2015:13:12:16 +0000] "GET / HTTP/1.1" 200 0 4700 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0" 10.230.31.8:43601 x_forwarded_for:"172.21.27.221" vcap_request_id:f807d34a-9191-4a29-5831-cb78eece72e1 response_time:0.008955044 app_id:9011c83a-9407-4e0a-ae91-0c66ff3d6e92

I use Gorouter 212 source and Golang 1.4.2 source.

What confuses me is that here(https://github.com/golang/go/blob/release-branch.go1.4/src/net/http/httputil/reverseproxy.go#L110) we set outreq to req, and here(https://github.com/golang/go/blob/release-branch.go1.4/src/net/http/httputil/reverseproxy.go#L142) we set the outreq's X-Forwareded-For headers that affect req's X-Forwareded-For header when using curl/w3m/lynx but not with Firefox/Opera/Chrome/wget

Anyone ever seen anything similar or anything obvious I have missed?


Simon Johansson <simon@...>
 

Gist of diff:
https://gist.github.com/simonjohansson/847e972f1459ea4cd65e

Gist of debug output:
https://gist.github.com/simonjohansson/3324b08fc42e5ac7105a

Since it's quite hard to read in the email...


Dieu Cao <dcao@...>
 

What do you use for your edge load balancer?
You could try to set up your edge load balancer to strip x_forwarded_for
sent in the request and set it again before forwarding on.
In that way, I believe it should be consistent, whatever the client.

-Dieu

On Wed, Aug 19, 2015 at 7:38 AM, Simon Johansson <simon(a)simonjohansson.com>
wrote:

Gist of diff:
https://gist.github.com/simonjohansson/847e972f1459ea4cd65e

Gist of debug output:
https://gist.github.com/simonjohansson/3324b08fc42e5ac7105a

Since it's quite hard to read in the email...


Simon Johansson <simon@...>
 

Dieu,

We use F5, just tried with the release HAProxy and I get concictent X-Forwarded-For between clients! Dont know how I managed to forget to check that. :)

In that way, I believe it should be consistent, whatever the client.
So in the HAProxy config the header gets updated/inserted with

frontend http-in
....
option forwardfor

In the HTTP profile in F5 we have " Insert X-Forwarded-For " enabled which is basically the same as the HAProxy config option. No irules to modify the header. But I get different results depending on the client.

How curious....


Simon Johansson <simon@...>
 

Disregarding F5/HAProxy comletely and going directly(10.230.18.134) to a Gorouter also yields different results depending on the client.

$ curl -H "host:cf-env.domain.com" 10.230.18.134
$ wget --header=host:cf-env.domain.com -qO- 10.230.18.134

Results in the following gorouter logs

cf-env.domain.com - [20/08/2015:13:32:57 +0000] "GET / HTTP/1.1" 200 0 5576 "-" "curl/7.43.0" 172.21.27.221:41193 x_forwarded_for:"172.21.27.221" ...

cf-env.domain.com - [20/08/2015:13:35:47 +0000] "GET / HTTP/1.1" 200 0 5655 "-" "Wget/1.16.3 (linux-gnu)" 172.21.27.221:41218 x_forwarded_for:"-" ....

I.e when using curl the x_forwarded_for header is set, but when using wget its not.

Doing a tcpdump on the gorouter we get these headers for the incoming request.

GET / HTTP/1.1
host:cf-env.domain.com
User-Agent: curl/7.43.0
Accept: */*

GET / HTTP/1.1
User-Agent: Wget/1.16.3 (linux-gnu)
Accept: */*
Accept-Encoding: identity
host: cf-env.domain.com
Connection: Keep-Alive

As we can see there is two(well, three if you cound User-Agent) differing headers, the wget request have the Accept-Encoding and Connection headers whereas the curl request does not.

So, when going directly to a gorouter with curl(that previously set the x_forwarded_for header) and setting the Connection header to Keep-Alive we get this

$ curl -H "host:cf-env.domain.com" -H "Connection: Keep-Alive" 10.230.18.134

GET / HTTP/1.1
host:cf-env.domain.com
User-Agent: curl/7.43.0
Accept: */*
Connection: Keep-Alive

cf-env.domain.com - [20/08/2015:13:46:25 +0000] "GET /curl-keep-alive HTTP/1.1" 404 0 18 "-" "curl/7.43.0" 172.21.27.221:41313 x_forwarded_for:"-" ...

So, Keep-Alive seems to be the difference between getting x_forwarded_for set or not in this case.

Interestingly, when hitting an app with either wget or curl via HAProxy(that both logs x_forwarded_for: sourceIP, HAProxyIP) we can see the headers for the requests on the Gorouter have Connection unset.

$ # On my machine
$ curl -H "host:cf-env.domain.com" 10.230.18.68/curl
$ curl -H "host:cf-env.domain.com" -H "Connection: Keep-Alive" 10.230.18.68/curl-keep-alive-set
$ wget --header=host:cf-env.domain.com -qO- 10.230.18.68/wget

$ # Tcpdump on HAProxy
GET /curl HTTP/1.1
host: cf-env.domain.com
User-Agent: curl/7.43.0
Accept: */*

GET /curl-keep-alive-set HTTP/1.1
host: cf-env.domain.com
User-Agent: curl/7.43.0
Accept: */*
Connection: Keep-Alive

GET /wget HTTP/1.1
User-Agent: Wget/1.16.3 (linux-gnu)
Accept: */*
Accept-Encoding: identity
host: cf-env.domain.com
Connection: Keep-Alive


$ # Tcpdump on Gorouter
GET /curl HTTP/1.1
User-Agent: curl/7.43.0
Accept: */*
host: cf-env.domain.com
X-Forwarded-Proto: http
X-Forwarded-For: 172.21.27.221

GET /curl-keep-alive-set HTTP/1.1
User-Agent: curl/7.43.0
Accept: */*
host: cf-env.domain.com
X-Forwarded-Proto: http
X-Forwarded-For: 172.21.27.221

GET /wget HTTP/1.1
User-Agent: Wget/1.16.3 (linux-gnu)
Accept: */*
Accept-Encoding: identity
host: cf-env.domain.com
X-Forwarded-Proto: http
X-Forwarded-For: 172.21.27.221

$ cf logs cf-env
cf-env.domain.com - [20/08/2015:14:10:12 +0000] "GET /curl HTTP/1.1" 404 0 18 "-" "curl/7.43.0" 10.230.18.68:60573 x_forwarded_for:"172.21.27.221, 10.230.18.68" ...
cf-env.domain.com - [20/08/2015:14:10:17 +0000] "GET /curl-keep-alive-set HTTP/1.1" 404 0 18 "-" "curl/7.43.0" 10.230.18.68:60594 x_forwarded_for:"172.21.27.221, 10.230.18.68" ...
cf-env.domain.com - [20/08/2015:14:10:22 +0000] "GET /wget HTTP/1.1" 404 0 18 "-" "Wget/1.16.3 (linux-gnu)" 10.230.18.68:60615 x_forwarded_for:"172.21.27.221, 10.230.18.68" ...

So it seems HAProxy is stripping the Connection header before forwarding it to a Gorouter and that is why we dont get different x_forwarded_for ips depending on client whereas we get different when using F5 as it doesnt strip the Connection header.


Christopher Piraino <cpiraino@...>
 

Simon,

That is very interesting, nice investigation. I would have to do a little
digging to try and see what exactly is going on, but the fact that the
GoRouter is not setting the "X-Forwarded-For" header when keep-alive is
enabled sounds suspiciously similar to an issue with HaProxy
<http://comments.gmane.org/gmane.comp.web.haproxy/5419> where it was only
adding the x-forwarded-proto on the first request for a connection. Not
sure if this is actually similar though.

I took a quick look through golang's reverseproxy code and nothing jumped
out at me, I added a story in our backlog
<https://www.pivotaltracker.com/story/show/101692478> to investigate
further.

- Chris Piraino

On Thu, Aug 20, 2015 at 7:20 AM, Simon Johansson <simon(a)simonjohansson.com>
wrote:

Disregarding F5/HAProxy comletely and going directly(10.230.18.134) to a
Gorouter also yields different results depending on the client.

$ curl -H "host:cf-env.domain.com" 10.230.18.134
$ wget --header=host:cf-env.domain.com -qO- 10.230.18.134

Results in the following gorouter logs

cf-env.domain.com - [20/08/2015:13:32:57 +0000] "GET / HTTP/1.1" 200 0
5576 "-" "curl/7.43.0" 172.21.27.221:41193
x_forwarded_for:"172.21.27.221" ...

cf-env.domain.com - [20/08/2015:13:35:47 +0000] "GET / HTTP/1.1" 200 0
5655 "-" "Wget/1.16.3 (linux-gnu)" 172.21.27.221:41218
x_forwarded_for:"-" ....

I.e when using curl the x_forwarded_for header is set, but when using wget
its not.

Doing a tcpdump on the gorouter we get these headers for the incoming
request.

GET / HTTP/1.1
host:cf-env.domain.com
User-Agent: curl/7.43.0
Accept: */*

GET / HTTP/1.1
User-Agent: Wget/1.16.3 (linux-gnu)
Accept: */*
Accept-Encoding: identity
host: cf-env.domain.com
Connection: Keep-Alive

As we can see there is two(well, three if you cound User-Agent) differing
headers, the wget request have the Accept-Encoding and Connection headers
whereas the curl request does not.

So, when going directly to a gorouter with curl(that previously set the
x_forwarded_for header) and setting the Connection header to Keep-Alive we
get this

$ curl -H "host:cf-env.domain.com" -H "Connection: Keep-Alive"
10.230.18.134

GET / HTTP/1.1
host:cf-env.domain.com
User-Agent: curl/7.43.0
Accept: */*
Connection: Keep-Alive

cf-env.domain.com - [20/08/2015:13:46:25 +0000] "GET /curl-keep-alive
HTTP/1.1" 404 0 18 "-" "curl/7.43.0" 172.21.27.221:41313
x_forwarded_for:"-" ...

So, Keep-Alive seems to be the difference between getting x_forwarded_for
set or not in this case.

Interestingly, when hitting an app with either wget or curl via
HAProxy(that both logs x_forwarded_for: sourceIP, HAProxyIP) we can see the
headers for the requests on the Gorouter have Connection unset.

$ # On my machine
$ curl -H "host:cf-env.domain.com" 10.230.18.68/curl
$ curl -H "host:cf-env.domain.com" -H "Connection: Keep-Alive"
10.230.18.68/curl-keep-alive-set
$ wget --header=host:cf-env.domain.com -qO- 10.230.18.68/wget

$ # Tcpdump on HAProxy
GET /curl HTTP/1.1
host: cf-env.domain.com
User-Agent: curl/7.43.0
Accept: */*

GET /curl-keep-alive-set HTTP/1.1
host: cf-env.domain.com
User-Agent: curl/7.43.0
Accept: */*
Connection: Keep-Alive

GET /wget HTTP/1.1
User-Agent: Wget/1.16.3 (linux-gnu)
Accept: */*
Accept-Encoding: identity
host: cf-env.domain.com
Connection: Keep-Alive


$ # Tcpdump on Gorouter
GET /curl HTTP/1.1
User-Agent: curl/7.43.0
Accept: */*
host: cf-env.domain.com
X-Forwarded-Proto: http
X-Forwarded-For: 172.21.27.221

GET /curl-keep-alive-set HTTP/1.1
User-Agent: curl/7.43.0
Accept: */*
host: cf-env.domain.com
X-Forwarded-Proto: http
X-Forwarded-For: 172.21.27.221

GET /wget HTTP/1.1
User-Agent: Wget/1.16.3 (linux-gnu)
Accept: */*
Accept-Encoding: identity
host: cf-env.domain.com
X-Forwarded-Proto: http
X-Forwarded-For: 172.21.27.221

$ cf logs cf-env
cf-env.domain.com - [20/08/2015:14:10:12 +0000] "GET /curl HTTP/1.1" 404
0 18 "-" "curl/7.43.0" 10.230.18.68:60573 x_forwarded_for:"172.21.27.221,
10.230.18.68" ...
cf-env.domain.com - [20/08/2015:14:10:17 +0000] "GET /curl-keep-alive-set
HTTP/1.1" 404 0 18 "-" "curl/7.43.0" 10.230.18.68:60594
x_forwarded_for:"172.21.27.221, 10.230.18.68" ...
cf-env.domain.com - [20/08/2015:14:10:22 +0000] "GET /wget HTTP/1.1" 404
0 18 "-" "Wget/1.16.3 (linux-gnu)" 10.230.18.68:60615
x_forwarded_for:"172.21.27.221, 10.230.18.68" ...

So it seems HAProxy is stripping the Connection header before forwarding
it to a Gorouter and that is why we dont get different x_forwarded_for ips
depending on client whereas we get different when using F5 as it doesnt
strip the Connection header.


Simon Johansson <simon@...>
 

Chris, I now know why this is happening.

In ServeHTTP in reverseproxy in the httputil package[1] we make a copy of
the incoming request for the outgoing request[2]. We then check[3] if the
incoming request have any "Hop-by-hop"[4] headers are set.

If they are set we assign the outgoing requests headers to a new Header
object[5], deep copy all the headers into it from the incoming request and
delete the Hop-by-hop headers.

After this we update the X-Forwarded-For header[6].

This effectively means that if the incoming request doesn't container any
of the hop-by-hop headers we will in step [6] update the incoming requests
X-Forwarded-For header as outrec.Header is a reference to rec.Header. But
in the case we do have a hop-by-hop header the inrec.Headers,
outrec.Headers will not be a reference and thus we get different logs when
we enter the defer in gorouter/proxy/proxy.go[7]

I have emailed Brad Fitzpatrick who was involved in writing the reverse
proxy to see if this is expected behaviour, to me it seems like a bug?

[1]
https://github.com/golang/go/blob/release-branch.go1.4/src/net/http/httputil/reverseproxy.go
[2]
https://github.com/golang/go/blob/release-branch.go1.4/src/net/http/httputil/reverseproxy.go#L110
[3]
https://github.com/golang/go/blob/release-branch.go1.4/src/net/http/httputil/reverseproxy.go#L125
[4]
https://github.com/golang/go/blob/release-branch.go1.4/src/net/http/httputil/reverseproxy.go#L92
[5]
https://github.com/golang/go/blob/release-branch.go1.4/src/net/http/httputil/reverseproxy.go#L127
[6]
https://github.com/golang/go/blob/release-branch.go1.4/src/net/http/httputil/reverseproxy.go#L142
[7] https://github.com/cloudfoundry/gorouter/blob/master/proxy/proxy.go#L149

On Thu, Aug 20, 2015 at 6:05 PM, Christopher Piraino <cpiraino(a)pivotal.io>
wrote:

Simon,

That is very interesting, nice investigation. I would have to do a little
digging to try and see what exactly is going on, but the fact that the
GoRouter is not setting the "X-Forwarded-For" header when keep-alive is
enabled sounds suspiciously similar to an issue with HaProxy
<http://comments.gmane.org/gmane.comp.web.haproxy/5419> where it was only
adding the x-forwarded-proto on the first request for a connection. Not
sure if this is actually similar though.

I took a quick look through golang's reverseproxy code and nothing jumped
out at me, I added a story in our backlog
<https://www.pivotaltracker.com/story/show/101692478> to investigate
further.

- Chris Piraino

On Thu, Aug 20, 2015 at 7:20 AM, Simon Johansson <simon(a)simonjohansson.com
wrote:
Disregarding F5/HAProxy comletely and going directly(10.230.18.134) to a
Gorouter also yields different results depending on the client.

$ curl -H "host:cf-env.domain.com" 10.230.18.134
$ wget --header=host:cf-env.domain.com -qO- 10.230.18.134

Results in the following gorouter logs

cf-env.domain.com - [20/08/2015:13:32:57 +0000] "GET / HTTP/1.1" 200 0
5576 "-" "curl/7.43.0" 172.21.27.221:41193
x_forwarded_for:"172.21.27.221" ...

cf-env.domain.com - [20/08/2015:13:35:47 +0000] "GET / HTTP/1.1" 200 0
5655 "-" "Wget/1.16.3 (linux-gnu)" 172.21.27.221:41218
x_forwarded_for:"-" ....

I.e when using curl the x_forwarded_for header is set, but when using
wget its not.

Doing a tcpdump on the gorouter we get these headers for the incoming
request.

GET / HTTP/1.1
host:cf-env.domain.com
User-Agent: curl/7.43.0
Accept: */*

GET / HTTP/1.1
User-Agent: Wget/1.16.3 (linux-gnu)
Accept: */*
Accept-Encoding: identity
host: cf-env.domain.com
Connection: Keep-Alive

As we can see there is two(well, three if you cound User-Agent) differing
headers, the wget request have the Accept-Encoding and Connection headers
whereas the curl request does not.

So, when going directly to a gorouter with curl(that previously set the
x_forwarded_for header) and setting the Connection header to Keep-Alive we
get this

$ curl -H "host:cf-env.domain.com" -H "Connection: Keep-Alive"
10.230.18.134

GET / HTTP/1.1
host:cf-env.domain.com
User-Agent: curl/7.43.0
Accept: */*
Connection: Keep-Alive

cf-env.domain.com - [20/08/2015:13:46:25 +0000] "GET /curl-keep-alive
HTTP/1.1" 404 0 18 "-" "curl/7.43.0" 172.21.27.221:41313
x_forwarded_for:"-" ...

So, Keep-Alive seems to be the difference between getting x_forwarded_for
set or not in this case.

Interestingly, when hitting an app with either wget or curl via
HAProxy(that both logs x_forwarded_for: sourceIP, HAProxyIP) we can see the
headers for the requests on the Gorouter have Connection unset.

$ # On my machine
$ curl -H "host:cf-env.domain.com" 10.230.18.68/curl
$ curl -H "host:cf-env.domain.com" -H "Connection: Keep-Alive"
10.230.18.68/curl-keep-alive-set
$ wget --header=host:cf-env.domain.com -qO- 10.230.18.68/wget

$ # Tcpdump on HAProxy
GET /curl HTTP/1.1
host: cf-env.domain.com
User-Agent: curl/7.43.0
Accept: */*

GET /curl-keep-alive-set HTTP/1.1
host: cf-env.domain.com
User-Agent: curl/7.43.0
Accept: */*
Connection: Keep-Alive

GET /wget HTTP/1.1
User-Agent: Wget/1.16.3 (linux-gnu)
Accept: */*
Accept-Encoding: identity
host: cf-env.domain.com
Connection: Keep-Alive


$ # Tcpdump on Gorouter
GET /curl HTTP/1.1
User-Agent: curl/7.43.0
Accept: */*
host: cf-env.domain.com
X-Forwarded-Proto: http
X-Forwarded-For: 172.21.27.221

GET /curl-keep-alive-set HTTP/1.1
User-Agent: curl/7.43.0
Accept: */*
host: cf-env.domain.com
X-Forwarded-Proto: http
X-Forwarded-For: 172.21.27.221

GET /wget HTTP/1.1
User-Agent: Wget/1.16.3 (linux-gnu)
Accept: */*
Accept-Encoding: identity
host: cf-env.domain.com
X-Forwarded-Proto: http
X-Forwarded-For: 172.21.27.221

$ cf logs cf-env
cf-env.domain.com - [20/08/2015:14:10:12 +0000] "GET /curl HTTP/1.1" 404
0 18 "-" "curl/7.43.0" 10.230.18.68:60573
x_forwarded_for:"172.21.27.221, 10.230.18.68" ...
cf-env.domain.com - [20/08/2015:14:10:17 +0000] "GET
/curl-keep-alive-set HTTP/1.1" 404 0 18 "-" "curl/7.43.0"
10.230.18.68:60594 x_forwarded_for:"172.21.27.221, 10.230.18.68" ...
cf-env.domain.com - [20/08/2015:14:10:22 +0000] "GET /wget HTTP/1.1" 404
0 18 "-" "Wget/1.16.3 (linux-gnu)" 10.230.18.68:60615
x_forwarded_for:"172.21.27.221, 10.230.18.68" ...

So it seems HAProxy is stripping the Connection header before forwarding
it to a Gorouter and that is why we dont get different x_forwarded_for ips
depending on client whereas we get different when using F5 as it doesnt
strip the Connection header.


Simon Johansson <simon@...>
 

I have now asked in golang-nuts[1] . It might take up to 24h for my message to appear, it will be called "Inconsistent X-Forwarded-For when using the httputil.ReverseProxy ?"

[1] https://groups.google.com/forum/#!forum/golang-nuts


Christopher Piraino <cpiraino@...>
 

Ah I see the problem. Nice find.

I'll await reply from Golang devs, but I'm wondering how much of a problem
this is for you? We could come up with a work around if this was hurting
you in some way.

- Chris

On Fri, Aug 21, 2015 at 3:00 AM, Simon Johansson <simon(a)simonjohansson.com>
wrote:

I have now asked in golang-nuts[1] . It might take up to 24h for my
message to appear, it will be called "Inconsistent X-Forwarded-For when
using the httputil.ReverseProxy ?"

[1] https://groups.google.com/forum/#!forum/golang-nuts


Simon Johansson <simon@...>
 

https://groups.google.com/forum/#!searchin/golang-nuts/Inconsistent$20X-Forwarded-For/golang-nuts/iUnZgzrOszI/ngxcLbSm1VYJ

Mail thread about the issue at golang-nuts.

This is not really a massive issue for us, the main use we have for this is to get the source IP, so whatewher comes after that is not really looked at in our env.

This issue is howewher quite confusing and its not obvious why there is inconsistency, I dont know how big of a deal that will be for other deployments. But the easy fix would be to copy setRequestXForwardedFor for proxy and just call it before we call p.accessLogger.Log in the defer in ServeHTTP.