Honor X-Forwarded-Port header - #2008
Conversation
Current coverage is 51.26% (diff: 94.17%)@@ master #2008 diff @@
==========================================
Files 125 126 +1
Lines 11136 11240 +104
Methods 0 0
Messages 0 0
Branches 0 0
==========================================
- Hits 6809 5762 -1047
- Misses 3443 4730 +1287
+ Partials 884 748 -136
|
|
Could you rebase to get the build fix please @miminar |
| host = strings.TrimSpace(hosts[0]) | ||
| } | ||
|
|
||
| forwardedPort := r.Header.Get("X-Forwarded-Port") |
There was a problem hiding this comment.
Isn't port a part of the host? How do these work together?
There was a problem hiding this comment.
Mostly it is. These headers aren't defined by any standard, so it's hard to give a precise meaning. Nevertheless, when proxy sets the X-Forwarded-Port it is an explicit proxy's port the client sent request to.
When X-Forwarded-Host lacks port, the explicit port shall be taken from X-Forwarded-Port if set. I'm not that sure about conflicting ports in both headers but I take explicit value as decisive.
There was a problem hiding this comment.
But, a port in the Host header is explicit, in that it is guaranteed to be set by a single entity, whereas X-Forward-Port may have been set by a secondary party.
| forwardedComboHeader.Set("X-Forwarded-Port", " 12345 \t") | ||
|
|
||
| forwardedMixedHeader := make(http.Header, 2) | ||
| forwardedMixedHeader.Set("X-Forwarded-Host", "first.example.com:5000") |
There was a problem hiding this comment.
This example concerns me. We clearly have a declared port in the host header but we now trust an unbound port? How do we know the same proxy added both headers?
There was a problem hiding this comment.
I don't think there's right or wrong answer to this. I find explicit value as better.
If there are two proxies, one overriding X-Forwarded-Host and the other overriding X-Forwarded-Port with conflicting ports, it is a clear misconfiguration.
There was a problem hiding this comment.
@miminar Should we not honor the "complete" host, rather than overriding the port? It seems like port should only apply if the host lacks a port but I am not sure what is expected. From a security perspective, the host must include a port, but I am not sure about how these headers are supposed to work.
Why are proxies stripping ports from requests? |
bd7aa9b to
ade8341
Compare
I cannot speak for all proxies. The default haproxy deployed by OpenShift strips the port from host headers as it is mostly running on default ones. If deployed to run on non-default port, there's still I don't know if it's the right approach. Keeping the port in host headers for non-default ports would certainly work as well. Nevertheless, honoring this header on application level, won't make the app worse. |
|
@miminar Ok, so it sounds like we should honor the Hopefully, you can fix the bug in openshift which is stripping the ports. Also, it looks like the implementation of |
It does, thanks!
Do you want to follow-up on that or shall I include it in this PR?
Yes. I'll include the support for |
That is up to you. It seems orthogonal. Thanks for taking care of this! |
ade8341 to
8a7f011
Compare
|
Included support for standard
@RichardScothern I see the |
8a7f011 to
f9767b0
Compare
| return appendValuesURL(up, values...).String() | ||
| } | ||
|
|
||
| // Following are states of forwarded header parser. |
There was a problem hiding this comment.
Put this in a separate file.
| // | ||
| // States marked with '*' are terminating. Any state could transition to a | ||
| // a failure which isn't listed among states. | ||
| const ( |
There was a problem hiding this comment.
Are these constants used outside of the parsing function? May just want to define them local to the function.
| } | ||
| } | ||
|
|
||
| func newHTTPRequest(t *testing.T, u *url.URL, headers ...string) *http.Request { |
There was a problem hiding this comment.
Just use a map literal. No need to create another way to specify an http request.
0c34ef7 to
eb1c254
Compare
|
Comments addressed. |
|
LGTM |
|
Parser logic looks good, since there is no unit tests directly with the regex, I would at least like to see the IPv6 case tested, with and without a port. |
eb1c254 to
3ebbd24
Compare
|
@dmcgowan Added support for ipv6 addresses and tests. I haven't tried to forward ipv6 address though. |
|
@miminar can you check the test failures please |
|
Ouch, the test passes for me with go 1.7. It looks like I need to tune it for go 1.6. |
3ebbd24 to
c621615
Compare
|
It should pass on both go16 and go17 now. |
Prefer non-standard headers like X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-Port over the standard Forwarded header to maintain backwards compatibility. If a port is not specified neither in Host nor in forwarded headers but it is specified just with X-Forwarded-Port, use its value in base urls for redirects. Forwarded header is defined in rfc7239. X-Forwarded-Port is a non-standard header. Here's a description copied from "HTTP Headers and Elastic Load Balancing" of AWS ELB docs: > The X-Forwarded-Port request header helps you identify the port that > an HTTP or HTTPS load balancer uses to connect to the client. Signed-off-by: Michal Minář <miminar@redhat.com>
c621615 to
1b43e1e
Compare
|
thanks @miminar LGTM |
|
LGTM |
If the explicit port is specified using this header, use it as a base for redirect urls.
As documented in article "HTTP Headers and Elastic Load Balancing" of AWS ELB docs:
This makes registry behave correctly when proxy strips port from the requests e.g.:
Where original request (docker daemon -> registry's proxy) looks like this:
Without this patch, during a
POST /v2/pjoe/hello-world/blobs/uploads/ HTTP/1.1request, registry would redirect to port-less urlregistry.f24-ose.vmwhich would make docker daemon attempt to upload on port 80 and fail.With this patch, registry will reply with correct location: