fix: process all X-Forwarded-For headers in the request

This commit is contained in:
Kevin Pollet 2021-12-14 15:36:07 +01:00 committed by GitHub
parent 54c77ecb54
commit be44385b42
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 107 additions and 81 deletions

View file

@ -165,6 +165,12 @@ func (x *XForwarded) rewrite(outreq *http.Request) {
unsafeHeader(outreq.Header).Set(xForwardedHost, outreq.Host) unsafeHeader(outreq.Header).Set(xForwardedHost, outreq.Host)
} }
// Per https://www.rfc-editor.org/rfc/rfc2616#section-4.2, the Forwarded IPs list is in
// the same order as the values in the X-Forwarded-For header(s).
if xffs := unsafeHeader(outreq.Header).Values(xForwardedFor); len(xffs) > 0 {
unsafeHeader(outreq.Header).Set(xForwardedFor, strings.Join(xffs, ", "))
}
if x.hostname != "" { if x.hostname != "" {
unsafeHeader(outreq.Header).Set(xForwardedServer, x.hostname) unsafeHeader(outreq.Header).Set(xForwardedServer, x.hostname)
} }
@ -198,6 +204,10 @@ func (h unsafeHeader) Get(key string) string {
return h[key][0] return h[key][0]
} }
func (h unsafeHeader) Values(key string) []string {
return h[key]
}
func (h unsafeHeader) Del(key string) { func (h unsafeHeader) Del(key string) {
delete(h, key) delete(h, key)
} }

View file

@ -15,7 +15,7 @@ func TestServeHTTP(t *testing.T) {
desc string desc string
insecure bool insecure bool
trustedIps []string trustedIps []string
incomingHeaders map[string]string incomingHeaders map[string][]string
remoteAddr string remoteAddr string
expectedHeaders map[string]string expectedHeaders map[string]string
tls bool tls bool
@ -27,13 +27,13 @@ func TestServeHTTP(t *testing.T) {
insecure: true, insecure: true,
trustedIps: nil, trustedIps: nil,
remoteAddr: "", remoteAddr: "",
incomingHeaders: map[string]string{}, incomingHeaders: map[string][]string{},
expectedHeaders: map[string]string{ expectedHeaders: map[string]string{
"X-Forwarded-for": "", xForwardedFor: "",
"X-Forwarded-Uri": "", xForwardedURI: "",
"X-Forwarded-Method": "", xForwardedMethod: "",
"X-Forwarded-Tls-Client-Cert": "", xForwardedTLSClientCert: "",
"X-Forwarded-Tls-Client-Cert-Info": "", xForwardedTLSClientCertInfo: "",
}, },
}, },
{ {
@ -41,19 +41,19 @@ func TestServeHTTP(t *testing.T) {
insecure: true, insecure: true,
trustedIps: nil, trustedIps: nil,
remoteAddr: "", remoteAddr: "",
incomingHeaders: map[string]string{ incomingHeaders: map[string][]string{
"X-Forwarded-for": "10.0.1.0, 10.0.1.12", xForwardedFor: {"10.0.1.0, 10.0.1.12"},
"X-Forwarded-Uri": "/bar", xForwardedURI: {"/bar"},
"X-Forwarded-Method": "GET", xForwardedMethod: {"GET"},
"X-Forwarded-Tls-Client-Cert": "Cert", xForwardedTLSClientCert: {"Cert"},
"X-Forwarded-Tls-Client-Cert-Info": "CertInfo", xForwardedTLSClientCertInfo: {"CertInfo"},
}, },
expectedHeaders: map[string]string{ expectedHeaders: map[string]string{
"X-Forwarded-for": "10.0.1.0, 10.0.1.12", xForwardedFor: "10.0.1.0, 10.0.1.12",
"X-Forwarded-Uri": "/bar", xForwardedURI: "/bar",
"X-Forwarded-Method": "GET", xForwardedMethod: "GET",
"X-Forwarded-Tls-Client-Cert": "Cert", xForwardedTLSClientCert: "Cert",
"X-Forwarded-Tls-Client-Cert-Info": "CertInfo", xForwardedTLSClientCertInfo: "CertInfo",
}, },
}, },
{ {
@ -61,19 +61,19 @@ func TestServeHTTP(t *testing.T) {
insecure: false, insecure: false,
trustedIps: nil, trustedIps: nil,
remoteAddr: "", remoteAddr: "",
incomingHeaders: map[string]string{ incomingHeaders: map[string][]string{
"X-Forwarded-for": "10.0.1.0, 10.0.1.12", xForwardedFor: {"10.0.1.0, 10.0.1.12"},
"X-Forwarded-Uri": "/bar", xForwardedURI: {"/bar"},
"X-Forwarded-Method": "GET", xForwardedMethod: {"GET"},
"X-Forwarded-Tls-Client-Cert": "Cert", xForwardedTLSClientCert: {"Cert"},
"X-Forwarded-Tls-Client-Cert-Info": "CertInfo", xForwardedTLSClientCertInfo: {"CertInfo"},
}, },
expectedHeaders: map[string]string{ expectedHeaders: map[string]string{
"X-Forwarded-for": "", xForwardedFor: "",
"X-Forwarded-Uri": "", xForwardedURI: "",
"X-Forwarded-Method": "", xForwardedMethod: "",
"X-Forwarded-Tls-Client-Cert": "", xForwardedTLSClientCert: "",
"X-Forwarded-Tls-Client-Cert-Info": "", xForwardedTLSClientCertInfo: "",
}, },
}, },
{ {
@ -81,19 +81,19 @@ func TestServeHTTP(t *testing.T) {
insecure: false, insecure: false,
trustedIps: []string{"10.0.1.100"}, trustedIps: []string{"10.0.1.100"},
remoteAddr: "10.0.1.100:80", remoteAddr: "10.0.1.100:80",
incomingHeaders: map[string]string{ incomingHeaders: map[string][]string{
"X-Forwarded-for": "10.0.1.0, 10.0.1.12", xForwardedFor: {"10.0.1.0, 10.0.1.12"},
"X-Forwarded-Uri": "/bar", xForwardedURI: {"/bar"},
"X-Forwarded-Method": "GET", xForwardedMethod: {"GET"},
"X-Forwarded-Tls-Client-Cert": "Cert", xForwardedTLSClientCert: {"Cert"},
"X-Forwarded-Tls-Client-Cert-Info": "CertInfo", xForwardedTLSClientCertInfo: {"CertInfo"},
}, },
expectedHeaders: map[string]string{ expectedHeaders: map[string]string{
"X-Forwarded-for": "10.0.1.0, 10.0.1.12", xForwardedFor: "10.0.1.0, 10.0.1.12",
"X-Forwarded-Uri": "/bar", xForwardedURI: "/bar",
"X-Forwarded-Method": "GET", xForwardedMethod: "GET",
"X-Forwarded-Tls-Client-Cert": "Cert", xForwardedTLSClientCert: "Cert",
"X-Forwarded-Tls-Client-Cert-Info": "CertInfo", xForwardedTLSClientCertInfo: "CertInfo",
}, },
}, },
{ {
@ -101,19 +101,19 @@ func TestServeHTTP(t *testing.T) {
insecure: false, insecure: false,
trustedIps: []string{"10.0.1.100"}, trustedIps: []string{"10.0.1.100"},
remoteAddr: "10.0.1.101:80", remoteAddr: "10.0.1.101:80",
incomingHeaders: map[string]string{ incomingHeaders: map[string][]string{
"X-Forwarded-for": "10.0.1.0, 10.0.1.12", xForwardedFor: {"10.0.1.0, 10.0.1.12"},
"X-Forwarded-Uri": "/bar", xForwardedURI: {"/bar"},
"X-Forwarded-Method": "GET", xForwardedMethod: {"GET"},
"X-Forwarded-Tls-Client-Cert": "Cert", xForwardedTLSClientCert: {"Cert"},
"X-Forwarded-Tls-Client-Cert-Info": "CertInfo", xForwardedTLSClientCertInfo: {"CertInfo"},
}, },
expectedHeaders: map[string]string{ expectedHeaders: map[string]string{
"X-Forwarded-for": "", xForwardedFor: "",
"X-Forwarded-Uri": "", xForwardedURI: "",
"X-Forwarded-Method": "", xForwardedMethod: "",
"X-Forwarded-Tls-Client-Cert": "", xForwardedTLSClientCert: "",
"X-Forwarded-Tls-Client-Cert-Info": "", xForwardedTLSClientCertInfo: "",
}, },
}, },
{ {
@ -121,19 +121,19 @@ func TestServeHTTP(t *testing.T) {
insecure: false, insecure: false,
trustedIps: []string{"1.2.3.4/24"}, trustedIps: []string{"1.2.3.4/24"},
remoteAddr: "1.2.3.156:80", remoteAddr: "1.2.3.156:80",
incomingHeaders: map[string]string{ incomingHeaders: map[string][]string{
"X-Forwarded-for": "10.0.1.0, 10.0.1.12", xForwardedFor: {"10.0.1.0, 10.0.1.12"},
"X-Forwarded-Uri": "/bar", xForwardedURI: {"/bar"},
"X-Forwarded-Method": "GET", xForwardedMethod: {"GET"},
"X-Forwarded-Tls-Client-Cert": "Cert", xForwardedTLSClientCert: {"Cert"},
"X-Forwarded-Tls-Client-Cert-Info": "CertInfo", xForwardedTLSClientCertInfo: {"CertInfo"},
}, },
expectedHeaders: map[string]string{ expectedHeaders: map[string]string{
"X-Forwarded-for": "10.0.1.0, 10.0.1.12", xForwardedFor: "10.0.1.0, 10.0.1.12",
"X-Forwarded-Uri": "/bar", xForwardedURI: "/bar",
"X-Forwarded-Method": "GET", xForwardedMethod: "GET",
"X-Forwarded-Tls-Client-Cert": "Cert", xForwardedTLSClientCert: "Cert",
"X-Forwarded-Tls-Client-Cert-Info": "CertInfo", xForwardedTLSClientCertInfo: "CertInfo",
}, },
}, },
{ {
@ -141,19 +141,33 @@ func TestServeHTTP(t *testing.T) {
insecure: false, insecure: false,
trustedIps: []string{"1.2.3.4/24"}, trustedIps: []string{"1.2.3.4/24"},
remoteAddr: "10.0.1.101:80", remoteAddr: "10.0.1.101:80",
incomingHeaders: map[string]string{ incomingHeaders: map[string][]string{
"X-Forwarded-for": "10.0.1.0, 10.0.1.12", xForwardedFor: {"10.0.1.0, 10.0.1.12"},
"X-Forwarded-Uri": "/bar", xForwardedURI: {"/bar"},
"X-Forwarded-Method": "GET", xForwardedMethod: {"GET"},
"X-Forwarded-Tls-Client-Cert": "Cert", xForwardedTLSClientCert: {"Cert"},
"X-Forwarded-Tls-Client-Cert-Info": "CertInfo", xForwardedTLSClientCertInfo: {"CertInfo"},
}, },
expectedHeaders: map[string]string{ expectedHeaders: map[string]string{
"X-Forwarded-for": "", xForwardedFor: "",
"X-Forwarded-Uri": "", xForwardedURI: "",
"X-Forwarded-Method": "", xForwardedMethod: "",
"X-Forwarded-Tls-Client-Cert": "", xForwardedTLSClientCert: "",
"X-Forwarded-Tls-Client-Cert-Info": "", xForwardedTLSClientCertInfo: "",
},
},
{
desc: "xForwardedFor with multiple header(s) values",
insecure: true,
incomingHeaders: map[string][]string{
xForwardedFor: {
"10.0.0.4, 10.0.0.3",
"10.0.0.2, 10.0.0.1",
"10.0.0.0",
},
},
expectedHeaders: map[string]string{
xForwardedFor: "10.0.0.4, 10.0.0.3, 10.0.0.2, 10.0.0.1, 10.0.0.0",
}, },
}, },
{ {
@ -167,8 +181,8 @@ func TestServeHTTP(t *testing.T) {
desc: "xRealIP was already populated from previous headers", desc: "xRealIP was already populated from previous headers",
insecure: true, insecure: true,
remoteAddr: "10.0.1.101:80", remoteAddr: "10.0.1.101:80",
incomingHeaders: map[string]string{ incomingHeaders: map[string][]string{
xRealIP: "10.0.1.12", xRealIP: {"10.0.1.12"},
}, },
expectedHeaders: map[string]string{ expectedHeaders: map[string]string{
xRealIP: "10.0.1.12", xRealIP: "10.0.1.12",
@ -208,8 +222,8 @@ func TestServeHTTP(t *testing.T) {
desc: "xForwardedProto with websocket and tls and already x-forwarded-proto with wss", desc: "xForwardedProto with websocket and tls and already x-forwarded-proto with wss",
tls: true, tls: true,
websocket: true, websocket: true,
incomingHeaders: map[string]string{ incomingHeaders: map[string][]string{
xForwardedProto: "wss", xForwardedProto: {"wss"},
}, },
expectedHeaders: map[string]string{ expectedHeaders: map[string]string{
xForwardedProto: "wss", xForwardedProto: "wss",
@ -226,8 +240,8 @@ func TestServeHTTP(t *testing.T) {
desc: "xForwardedPort with implicit tls port from proto header", desc: "xForwardedPort with implicit tls port from proto header",
// setting insecure just so our initial xForwardedProto does not get cleaned // setting insecure just so our initial xForwardedProto does not get cleaned
insecure: true, insecure: true,
incomingHeaders: map[string]string{ incomingHeaders: map[string][]string{
xForwardedProto: "https", xForwardedProto: {"https"},
}, },
expectedHeaders: map[string]string{ expectedHeaders: map[string]string{
xForwardedProto: "https", xForwardedProto: "https",
@ -280,8 +294,10 @@ func TestServeHTTP(t *testing.T) {
req.Host = test.host req.Host = test.host
} }
for k, v := range test.incomingHeaders { for k, values := range test.incomingHeaders {
req.Header.Set(k, v) for _, value := range values {
req.Header.Add(k, value)
}
} }
m, err := NewXForwarded(test.insecure, test.trustedIps, m, err := NewXForwarded(test.insecure, test.trustedIps,