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>
This commit is contained in:
@@ -3,10 +3,11 @@ package security
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -75,18 +76,51 @@ func (g *Guard) WhiteList(f http.HandlerFunc) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetActualRemoteHost(r *http.Request) (host string, err error) {
|
func GetActualRemoteHost(r *http.Request) string {
|
||||||
host = r.Header.Get("HTTP_X_FORWARDED_FOR")
|
// 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 == "" {
|
if host == "" {
|
||||||
host = r.Header.Get("X-FORWARDED-FOR")
|
host = r.Header.Get("X-FORWARDED-FOR")
|
||||||
}
|
}
|
||||||
if strings.Contains(host, ",") {
|
if host != "" {
|
||||||
host = host[0:strings.Index(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 == "" {
|
if host == "" {
|
||||||
host, _, err = net.SplitHostPort(r.RemoteAddr)
|
host = r.Header.Get("X-Real-IP")
|
||||||
}
|
}
|
||||||
return
|
|
||||||
|
// 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 the host (can be IP or hostname, just like headers)
|
||||||
|
return host
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Guard) checkWhiteList(w http.ResponseWriter, r *http.Request) error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
host, err := GetActualRemoteHost(r)
|
host := GetActualRemoteHost(r)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("get actual remote host %s in checkWhiteList failed: %v", r.RemoteAddr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Check exact match first (works for both IPs and hostnames)
|
||||||
if _, ok := g.whiteListIp[host]; ok {
|
if _, ok := g.whiteListIp[host]; ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cidrnet := range g.whiteListCIDR {
|
// Check CIDR ranges (only for valid IP addresses)
|
||||||
// If the whitelist entry contains a "/" it
|
remote := net.ParseIP(host)
|
||||||
// is a CIDR range, and we should check the
|
if remote != nil {
|
||||||
remote := net.ParseIP(host)
|
for _, cidrnet := range g.whiteListCIDR {
|
||||||
if cidrnet.Contains(remote) {
|
// If the whitelist entry contains a "/" it
|
||||||
return nil
|
// 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)
|
glog.V(0).Infof("Not in whitelist: %s (original RemoteAddr: %s)", host, r.RemoteAddr)
|
||||||
return fmt.Errorf("Not in whitelist: %s", r.RemoteAddr)
|
return fmt.Errorf("Not in whitelist: %s", host)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Guard) UpdateWhiteList(whiteList []string) {
|
func (g *Guard) UpdateWhiteList(whiteList []string) {
|
||||||
|
|||||||
@@ -259,10 +259,8 @@ func (ms *MasterServer) proxyToLeader(f http.HandlerFunc) http.HandlerFunc {
|
|||||||
proxy := httputil.NewSingleHostReverseProxy(targetUrl)
|
proxy := httputil.NewSingleHostReverseProxy(targetUrl)
|
||||||
director := proxy.Director
|
director := proxy.Director
|
||||||
proxy.Director = func(req *http.Request) {
|
proxy.Director = func(req *http.Request) {
|
||||||
actualHost, err := security.GetActualRemoteHost(req)
|
actualHost := security.GetActualRemoteHost(req)
|
||||||
if err == nil {
|
req.Header.Set("HTTP_X_FORWARDED_FOR", actualHost)
|
||||||
req.Header.Set("HTTP_X_FORWARDED_FOR", actualHost)
|
|
||||||
}
|
|
||||||
director(req)
|
director(req)
|
||||||
}
|
}
|
||||||
proxy.Transport = util_http.GetGlobalHttpClient().GetClientTransport()
|
proxy.Transport = util_http.GetGlobalHttpClient().GetClientTransport()
|
||||||
|
|||||||
Reference in New Issue
Block a user