From 0703308270dad1c31c02f0b0e75c829156748ff8 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 6 Aug 2025 01:03:00 -0700 Subject: [PATCH] remote address parsing should handle special cases (#7101) * remote address parsing should handle special cases * handling ipv6 * simplify * Update weed/security/guard.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update weed/security/guard.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * x-real-ip * Update guard.go * fixes Hostname Whitelisting: Fully restored - supports localhost, example.com, etc. IP Whitelisting: Still works - supports exact IPs and CIDR ranges Header Support: Consistent handling of X-Forwarded-For, X-Real-IP * simplify * Update weed/security/guard.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update weed/security/guard.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update guard.go * adjust function signature * Update weed/security/guard.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * indention * skip empty host --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- weed/security/guard.go | 73 ++++++++++++++++++++++++++---------- weed/server/master_server.go | 6 +-- 2 files changed, 56 insertions(+), 23 deletions(-) diff --git a/weed/security/guard.go b/weed/security/guard.go index f92b10044..a857c8549 100644 --- a/weed/security/guard.go +++ b/weed/security/guard.go @@ -3,10 +3,11 @@ package security import ( "errors" "fmt" - "github.com/seaweedfs/seaweedfs/weed/glog" "net" "net/http" "strings" + + "github.com/seaweedfs/seaweedfs/weed/glog" ) var ( @@ -75,18 +76,51 @@ func (g *Guard) WhiteList(f http.HandlerFunc) http.HandlerFunc { } } -func GetActualRemoteHost(r *http.Request) (host string, err error) { - host = r.Header.Get("HTTP_X_FORWARDED_FOR") +func GetActualRemoteHost(r *http.Request) string { + // Check X-Forwarded-For headers first (may contain comma-separated IPs) + // HTTP_X_FORWARDED_FOR is used for SeaweedFS internal communication when master proxies to leader + host := r.Header.Get("HTTP_X_FORWARDED_FOR") if host == "" { host = r.Header.Get("X-FORWARDED-FOR") } - if strings.Contains(host, ",") { - host = host[0:strings.Index(host, ",")] + if host != "" { + for _, ipStr := range strings.Split(host, ",") { + host = strings.TrimSpace(ipStr) + if host != "" { + break + } + } } + + // If no valid IP from X-Forwarded-For, try X-Real-IP (single IP) if host == "" { - host, _, err = net.SplitHostPort(r.RemoteAddr) + host = r.Header.Get("X-Real-IP") + } + + // If we got a host from headers, use it (can be IP or hostname) + if host != "" { + if host = strings.TrimSpace(host); host != "" { + return host + } + } + + // If no host from headers, extract from RemoteAddr + host, _, err := net.SplitHostPort(r.RemoteAddr) + if err == nil { + return host + } + + // If SplitHostPort fails, it may be because of a missing port. + // We try to parse RemoteAddr as a raw host (IP or hostname). + host = strings.TrimSpace(r.RemoteAddr) + // It might be an IPv6 address without a port, but with brackets. + // e.g. "[::1]" + if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") { + host = host[1 : len(host)-1] } - return + + // Return the host (can be IP or hostname, just like headers) + return host } func (g *Guard) checkWhiteList(w http.ResponseWriter, r *http.Request) error { @@ -94,26 +128,27 @@ func (g *Guard) checkWhiteList(w http.ResponseWriter, r *http.Request) error { return nil } - host, err := GetActualRemoteHost(r) - if err != nil { - return fmt.Errorf("get actual remote host %s in checkWhiteList failed: %v", r.RemoteAddr, err) - } + host := GetActualRemoteHost(r) + // Check exact match first (works for both IPs and hostnames) if _, ok := g.whiteListIp[host]; ok { return nil } - for _, cidrnet := range g.whiteListCIDR { - // If the whitelist entry contains a "/" it - // is a CIDR range, and we should check the - remote := net.ParseIP(host) - if cidrnet.Contains(remote) { - return nil + // Check CIDR ranges (only for valid IP addresses) + remote := net.ParseIP(host) + if remote != nil { + for _, cidrnet := range g.whiteListCIDR { + // If the whitelist entry contains a "/" it + // is a CIDR range, and we should check the + if cidrnet.Contains(remote) { + return nil + } } } - glog.V(0).Infof("Not in whitelist: %s", r.RemoteAddr) - return fmt.Errorf("Not in whitelist: %s", r.RemoteAddr) + glog.V(0).Infof("Not in whitelist: %s (original RemoteAddr: %s)", host, r.RemoteAddr) + return fmt.Errorf("Not in whitelist: %s", host) } func (g *Guard) UpdateWhiteList(whiteList []string) { diff --git a/weed/server/master_server.go b/weed/server/master_server.go index 4f14c31bc..7d0d6f938 100644 --- a/weed/server/master_server.go +++ b/weed/server/master_server.go @@ -259,10 +259,8 @@ func (ms *MasterServer) proxyToLeader(f http.HandlerFunc) http.HandlerFunc { proxy := httputil.NewSingleHostReverseProxy(targetUrl) director := proxy.Director proxy.Director = func(req *http.Request) { - actualHost, err := security.GetActualRemoteHost(req) - if err == nil { - req.Header.Set("HTTP_X_FORWARDED_FOR", actualHost) - } + actualHost := security.GetActualRemoteHost(req) + req.Header.Set("HTTP_X_FORWARDED_FOR", actualHost) director(req) } proxy.Transport = util_http.GetGlobalHttpClient().GetClientTransport()