Skip to content

Commit e2efede

Browse files
Merge pull request #14319 from dmage/fwd-host
Automatic merge from submit-queue (batch tested with PRs 16052, 16105, 14319, 16106, 16104) Use forwarded Host header without any changes The Docker Server is sensitive to the presence of the port in a URL. If Docker Server is authorised to https://example.com/ and it is redirected to https://example.com:443, it might loose the Authorization header. So we should not remove nor add the port to the Host header. Fixes bug 1439614
2 parents 41eeb79 + 3d6c78d commit e2efede

File tree

2 files changed

+71
-12
lines changed

2 files changed

+71
-12
lines changed

pkg/util/httprequest/httprequest.go

+28-9
Original file line numberDiff line numberDiff line change
@@ -66,32 +66,43 @@ func SchemeHost(req *http.Request) (string /*scheme*/, string /*host*/) {
6666
return strings.TrimSpace(value)
6767
}
6868

69-
forwardedProto := forwarded("Proto")
70-
forwardedHost := forwarded("Host")
71-
// If both X-Forwarded-Host and X-Forwarded-Port are sent, use the explicit port info
72-
if forwardedPort := forwarded("Port"); len(forwardedHost) > 0 && len(forwardedPort) > 0 {
73-
if h, _, err := net.SplitHostPort(forwardedHost); err == nil {
74-
forwardedHost = net.JoinHostPort(h, forwardedPort)
75-
} else {
76-
forwardedHost = net.JoinHostPort(forwardedHost, forwardedPort)
77-
}
69+
hasExplicitHost := func(h string) bool {
70+
_, _, err := net.SplitHostPort(h)
71+
return err == nil
7872
}
7973

74+
forwardedHost := forwarded("Host")
8075
host := ""
76+
hostHadExplicitPort := false
8177
switch {
8278
case len(forwardedHost) > 0:
8379
host = forwardedHost
80+
hostHadExplicitPort = hasExplicitHost(host)
81+
82+
// If both X-Forwarded-Host and X-Forwarded-Port are sent, use the explicit port info
83+
if forwardedPort := forwarded("Port"); len(forwardedPort) > 0 {
84+
if h, _, err := net.SplitHostPort(forwardedHost); err == nil {
85+
host = net.JoinHostPort(h, forwardedPort)
86+
} else {
87+
host = net.JoinHostPort(forwardedHost, forwardedPort)
88+
}
89+
}
90+
8491
case len(req.Host) > 0:
8592
host = req.Host
93+
hostHadExplicitPort = hasExplicitHost(host)
94+
8695
case len(req.URL.Host) > 0:
8796
host = req.URL.Host
97+
hostHadExplicitPort = hasExplicitHost(host)
8898
}
8999

90100
port := ""
91101
if _, p, err := net.SplitHostPort(host); err == nil {
92102
port = p
93103
}
94104

105+
forwardedProto := forwarded("Proto")
95106
scheme := ""
96107
switch {
97108
case len(forwardedProto) > 0:
@@ -106,5 +117,13 @@ func SchemeHost(req *http.Request) (string /*scheme*/, string /*host*/) {
106117
scheme = "http"
107118
}
108119

120+
if !hostHadExplicitPort {
121+
if (scheme == "https" && port == "443") || (scheme == "http" && port == "80") {
122+
if hostWithoutPort, _, err := net.SplitHostPort(host); err == nil {
123+
host = hostWithoutPort
124+
}
125+
}
126+
}
127+
109128
return scheme, host
110129
}

pkg/util/httprequest/httprequest_test.go

+43-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func TestSchemeHost(t *testing.T) {
2525
},
2626
},
2727
expectedScheme: "https",
28-
expectedHost: "example.com:443",
28+
expectedHost: "example.com",
2929
},
3030
"X-Forwarded-Port overwrites X-Forwarded-Host port": {
3131
req: &http.Request{
@@ -51,7 +51,32 @@ func TestSchemeHost(t *testing.T) {
5151
},
5252
},
5353
expectedScheme: "https",
54-
expectedHost: "example.com:443",
54+
expectedHost: "example.com",
55+
},
56+
"stripped X-Forwarded-Host and X-Forwarded-Port with non-standard port": {
57+
req: &http.Request{
58+
URL: &url.URL{Path: "/"},
59+
Host: "127.0.0.1",
60+
Header: http.Header{
61+
"X-Forwarded-Host": []string{"example.com"},
62+
"X-Forwarded-Port": []string{"80"},
63+
"X-Forwarded-Proto": []string{"https"},
64+
},
65+
},
66+
expectedScheme: "https",
67+
expectedHost: "example.com:80",
68+
},
69+
"detect scheme from X-Forwarded-Port": {
70+
req: &http.Request{
71+
URL: &url.URL{Path: "/"},
72+
Host: "127.0.0.1",
73+
Header: http.Header{
74+
"X-Forwarded-Host": []string{"example.com"},
75+
"X-Forwarded-Port": []string{"443"},
76+
},
77+
},
78+
expectedScheme: "https",
79+
expectedHost: "example.com",
5580
},
5681

5782
"req host": {
@@ -156,7 +181,7 @@ func TestSchemeHost(t *testing.T) {
156181
},
157182
},
158183
expectedScheme: "http",
159-
expectedHost: "route-namespace.router.default.svc.cluster.local:80",
184+
expectedHost: "route-namespace.router.default.svc.cluster.local",
160185
},
161186
"haproxy edge terminated route -> svc -> non-tls pod": {
162187
req: &http.Request{
@@ -171,6 +196,21 @@ func TestSchemeHost(t *testing.T) {
171196
},
172197
},
173198
expectedScheme: "https",
199+
expectedHost: "route-namespace.router.default.svc.cluster.local",
200+
},
201+
"haproxy edge terminated route -> svc -> non-tls pod with the explicit port": {
202+
req: &http.Request{
203+
URL: &url.URL{Path: "/"},
204+
Host: "route-namespace.router.default.svc.cluster.local:443",
205+
Header: http.Header{
206+
"X-Forwarded-Host": []string{"route-namespace.router.default.svc.cluster.local:443"},
207+
"X-Forwarded-Port": []string{"443"},
208+
"X-Forwarded-Proto": []string{"https"},
209+
"Forwarded": []string{"for=172.18.2.57;host=route-namespace.router.default.svc.cluster.local:443;proto=https"},
210+
"X-Forwarded-For": []string{"172.18.2.57"},
211+
},
212+
},
213+
expectedScheme: "https",
174214
expectedHost: "route-namespace.router.default.svc.cluster.local:443",
175215
},
176216
"haproxy passthrough route -> svc -> tls pod": {

0 commit comments

Comments
 (0)