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

This commit is contained in:
Francis Lavoie 2024-03-21 12:54:25 -04:00 committed by GitHub
parent e65b97f55b
commit 63d597c09d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 34 additions and 7 deletions

View file

@ -902,9 +902,18 @@ func trustedRealClientIP(r *http.Request, headers []string, clientIP string) str
allValues := strings.Split(strings.Join(values, ","), ",") allValues := strings.Split(strings.Join(values, ","), ",")
// Get first valid left-most IP address // Get first valid left-most IP address
for _, ip := range allValues { for _, part := range allValues {
ip, _, _ = strings.Cut(strings.TrimSpace(ip), "%") // Some proxies may retain the port number, so split if possible
ipAddr, err := netip.ParseAddr(ip) host, _, err := net.SplitHostPort(part)
if err != nil {
host = part
}
// Remove any zone identifier from the IP address
host, _, _ = strings.Cut(strings.TrimSpace(host), "%")
// Parse the IP address
ipAddr, err := netip.ParseAddr(host)
if err != nil { if err != nil {
continue continue
} }
@ -921,11 +930,20 @@ func trustedRealClientIP(r *http.Request, headers []string, clientIP string) str
// remote address is returned. // remote address is returned.
func strictUntrustedClientIp(r *http.Request, headers []string, trusted []netip.Prefix, clientIP string) string { func strictUntrustedClientIp(r *http.Request, headers []string, trusted []netip.Prefix, clientIP string) string {
for _, headerName := range headers { for _, headerName := range headers {
ips := strings.Split(strings.Join(r.Header.Values(headerName), ","), ",") parts := strings.Split(strings.Join(r.Header.Values(headerName), ","), ",")
for i := len(ips) - 1; i >= 0; i-- { for i := len(parts) - 1; i >= 0; i-- {
ip, _, _ := strings.Cut(strings.TrimSpace(ips[i]), "%") // Some proxies may retain the port number, so split if possible
ipAddr, err := netip.ParseAddr(ip) host, _, err := net.SplitHostPort(parts[i])
if err != nil {
host = parts[i]
}
// Remove any zone identifier from the IP address
host, _, _ = strings.Cut(strings.TrimSpace(host), "%")
// Parse the IP address
ipAddr, err := netip.ParseAddr(host)
if err != nil { if err != nil {
continue continue
} }

View file

@ -188,6 +188,15 @@ func TestServer_TrustedRealClientIP_OneTrustedHeaderValidArray(t *testing.T) {
assert.Equal(t, ip, "1.1.1.1") assert.Equal(t, ip, "1.1.1.1")
} }
func TestServer_TrustedRealClientIP_IncludesPort(t *testing.T) {
req := httptest.NewRequest("GET", "/", nil)
req.RemoteAddr = "192.0.2.1:12345"
req.Header.Set("X-Forwarded-For", "1.1.1.1:1234")
ip := trustedRealClientIP(req, []string{"X-Forwarded-For"}, "192.0.2.1")
assert.Equal(t, ip, "1.1.1.1")
}
func TestServer_TrustedRealClientIP_SkipsInvalidIps(t *testing.T) { func TestServer_TrustedRealClientIP_SkipsInvalidIps(t *testing.T) {
req := httptest.NewRequest("GET", "/", nil) req := httptest.NewRequest("GET", "/", nil)
req.RemoteAddr = "192.0.2.1:12345" req.RemoteAddr = "192.0.2.1:12345"