@marcel91200 / caddy Mirror · read-only / commits / 63d597c0
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 12, 15:45 UTC

caddyhttp: Accept XFF header values with ports, when parsing client IP (#6183)

Francis Lavoie committed Mar 21, 2024 at 12:54 UTC 63d597c09d542f4344416b821dc742ddb5fda68f
2 files changed +34 -7
modules/caddyhttp/server.go
+25 -7
@@ -902,9 +902,18 @@ func trustedRealClientIP(r *http.Request, headers []string, clientIP string) str
902 allValues := strings.Split(strings.Join(values, ","), ",")
903
904 // Get first valid left-most IP address
905 - for _, ip := range allValues {
906 - ip, _, _ = strings.Cut(strings.TrimSpace(ip), "%")
907 - ipAddr, err := netip.ParseAddr(ip)
905 + for _, part := range allValues {
906 + // Some proxies may retain the port number, so split if possible
907 + host, _, err := net.SplitHostPort(part)
908 + if err != nil {
909 + host = part
910 + }
911 +
912 + // Remove any zone identifier from the IP address
913 + host, _, _ = strings.Cut(strings.TrimSpace(host), "%")
914 +
915 + // Parse the IP address
916 + ipAddr, err := netip.ParseAddr(host)
917 if err != nil {
918 continue
919 }
@@ -921,11 +930,20 @@ func trustedRealClientIP(r *http.Request, headers []string, clientIP string) str
930 // remote address is returned.
931 func strictUntrustedClientIp(r *http.Request, headers []string, trusted []netip.Prefix, clientIP string) string {
932 for _, headerName := range headers {
924 - ips := strings.Split(strings.Join(r.Header.Values(headerName), ","), ",")
933 + parts := strings.Split(strings.Join(r.Header.Values(headerName), ","), ",")
934 +
935 + for i := len(parts) - 1; i >= 0; i-- {
936 + // Some proxies may retain the port number, so split if possible
937 + host, _, err := net.SplitHostPort(parts[i])
938 + if err != nil {
939 + host = parts[i]
940 + }
941 +
942 + // Remove any zone identifier from the IP address
943 + host, _, _ = strings.Cut(strings.TrimSpace(host), "%")
944
926 - for i := len(ips) - 1; i >= 0; i-- {
927 - ip, _, _ := strings.Cut(strings.TrimSpace(ips[i]), "%")
928 - ipAddr, err := netip.ParseAddr(ip)
945 + // Parse the IP address
946 + ipAddr, err := netip.ParseAddr(host)
947 if err != nil {
948 continue
949 }
modules/caddyhttp/server_test.go
+9
@@ -188,6 +188,15 @@ func TestServer_TrustedRealClientIP_OneTrustedHeaderValidArray(t *testing.T) {
188 assert.Equal(t, ip, "1.1.1.1")
189 }
190
191 +func TestServer_TrustedRealClientIP_IncludesPort(t *testing.T) {
192 + req := httptest.NewRequest("GET", "/", nil)
193 + req.RemoteAddr = "192.0.2.1:12345"
194 + req.Header.Set("X-Forwarded-For", "1.1.1.1:1234")
195 + ip := trustedRealClientIP(req, []string{"X-Forwarded-For"}, "192.0.2.1")
196 +
197 + assert.Equal(t, ip, "1.1.1.1")
198 +}
199 +
200 func TestServer_TrustedRealClientIP_SkipsInvalidIps(t *testing.T) {
201 req := httptest.NewRequest("GET", "/", nil)
202 req.RemoteAddr = "192.0.2.1:12345"