mirror of
				https://github.com/caddyserver/caddy.git
				synced 2025-11-04 03:27:23 -05:00 
			
		
		
		
	proxy: Fix transparent pass-thru of existing X-Forwarded-For headers
* Fixes #1960 Transparent proxy not appending existing X-Forwarded-For header * Fixes #1960 Formatting Code
This commit is contained in:
		
							parent
							
								
									64c18a7c6c
								
							
						
					
					
						commit
						88edca65d3
					
				@ -913,6 +913,67 @@ func TestHostSimpleProxyNoHeaderForward(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestReverseProxyTransparentHeaders(t *testing.T) {
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		name               string
 | 
			
		||||
		remoteAddr         string
 | 
			
		||||
		forwardedForHeader string
 | 
			
		||||
		expected           []string
 | 
			
		||||
	}{
 | 
			
		||||
		{"No header", "192.168.0.1:80", "", []string{"192.168.0.1"}},
 | 
			
		||||
		{"Existing", "192.168.0.1:80", "1.1.1.1, 2.2.2.2", []string{"1.1.1.1, 2.2.2.2, 192.168.0.1"}},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
			testReverseProxyTransparentHeaders(t, tc.remoteAddr, tc.forwardedForHeader, tc.expected)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testReverseProxyTransparentHeaders(t *testing.T, remoteAddr, forwardedForHeader string, expected []string) {
 | 
			
		||||
	// Arrange
 | 
			
		||||
	log.SetOutput(ioutil.Discard)
 | 
			
		||||
	defer log.SetOutput(os.Stderr)
 | 
			
		||||
 | 
			
		||||
	var actualHeaders http.Header
 | 
			
		||||
	backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		actualHeaders = r.Header
 | 
			
		||||
	}))
 | 
			
		||||
	defer backend.Close()
 | 
			
		||||
 | 
			
		||||
	config := "proxy / " + backend.URL + " {\n transparent \n}"
 | 
			
		||||
 | 
			
		||||
	// make proxy
 | 
			
		||||
	upstreams, err := NewStaticUpstreams(caddyfile.NewDispenser("Testfile", strings.NewReader(config)), "")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Errorf("Expected no error. Got: %s", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// set up proxy
 | 
			
		||||
	p := &Proxy{
 | 
			
		||||
		Next:      httpserver.EmptyNext, // prevents panic in some cases when test fails
 | 
			
		||||
		Upstreams: upstreams,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// create request and response recorder
 | 
			
		||||
	r := httptest.NewRequest("GET", backend.URL, nil)
 | 
			
		||||
	r.RemoteAddr = remoteAddr
 | 
			
		||||
	if forwardedForHeader != "" {
 | 
			
		||||
		r.Header.Set("X-Forwarded-For", forwardedForHeader)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
 | 
			
		||||
	// Act
 | 
			
		||||
	p.ServeHTTP(w, r)
 | 
			
		||||
 | 
			
		||||
	// Assert
 | 
			
		||||
	if got := actualHeaders["X-Forwarded-For"]; !reflect.DeepEqual(got, expected) {
 | 
			
		||||
		t.Errorf("Transparent proxy response does not contain expected %v header: expect %v, but got %v",
 | 
			
		||||
			"X-Forwarded-For", expected, got)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestHostHeaderReplacedUsingForward(t *testing.T) {
 | 
			
		||||
	var requestHost string
 | 
			
		||||
	backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
@ -943,11 +1004,22 @@ func TestHostHeaderReplacedUsingForward(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestBasicAuth(t *testing.T) {
 | 
			
		||||
	basicAuthTestcase(t, nil, nil)
 | 
			
		||||
	basicAuthTestcase(t, nil, url.UserPassword("username", "password"))
 | 
			
		||||
	basicAuthTestcase(t, url.UserPassword("usename", "password"), nil)
 | 
			
		||||
	basicAuthTestcase(t, url.UserPassword("unused", "unused"),
 | 
			
		||||
		url.UserPassword("username", "password"))
 | 
			
		||||
	testCases := []struct {
 | 
			
		||||
		name         string
 | 
			
		||||
		upstreamUser *url.Userinfo
 | 
			
		||||
		clientUser   *url.Userinfo
 | 
			
		||||
	}{
 | 
			
		||||
		{"Nil Both", nil, nil},
 | 
			
		||||
		{"Nil Upstream User", nil, url.UserPassword("username", "password")},
 | 
			
		||||
		{"Nil Client User", url.UserPassword("usename", "password"), nil},
 | 
			
		||||
		{"Both Provided", url.UserPassword("unused", "unused"),
 | 
			
		||||
			url.UserPassword("username", "password")},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		t.Run(tc.name, func(t *testing.T) {
 | 
			
		||||
			basicAuthTestcase(t, tc.upstreamUser, tc.clientUser)
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func basicAuthTestcase(t *testing.T, upstreamUser, clientUser *url.Userinfo) {
 | 
			
		||||
 | 
			
		||||
@ -431,9 +431,10 @@ func parseBlock(c *caddyfile.Dispenser, u *staticUpstream, hasSrv bool) error {
 | 
			
		||||
		}
 | 
			
		||||
		u.downstreamHeaders.Add(header, value)
 | 
			
		||||
	case "transparent":
 | 
			
		||||
		// Note: X-Forwarded-For header is always being appended for proxy connections
 | 
			
		||||
		// See implementation of createUpstreamRequest in proxy.go
 | 
			
		||||
		u.upstreamHeaders.Add("Host", "{host}")
 | 
			
		||||
		u.upstreamHeaders.Add("X-Real-IP", "{remote}")
 | 
			
		||||
		u.upstreamHeaders.Add("X-Forwarded-For", "{remote}")
 | 
			
		||||
		u.upstreamHeaders.Add("X-Forwarded-Proto", "{scheme}")
 | 
			
		||||
	case "websocket":
 | 
			
		||||
		u.upstreamHeaders.Add("Connection", "{>Connection}")
 | 
			
		||||
 | 
			
		||||
@ -282,7 +282,8 @@ func TestStop(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParseBlock(t *testing.T) {
 | 
			
		||||
func TestParseBlockTransparent(t *testing.T) {
 | 
			
		||||
	// tests for transparent proxy presets
 | 
			
		||||
	r, _ := http.NewRequest("GET", "/", nil)
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		config string
 | 
			
		||||
@ -316,6 +317,10 @@ func TestParseBlock(t *testing.T) {
 | 
			
		||||
			if _, ok := headers["X-Forwarded-Proto"]; !ok {
 | 
			
		||||
				t.Errorf("Test %d: Could not find the X-Forwarded-Proto header", i+1)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if _, ok := headers["X-Forwarded-For"]; ok {
 | 
			
		||||
				t.Errorf("Test %d: Found unexpected X-Forwarded-For header", i+1)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user