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"