mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-20 08:03:55 +08:00
123 lines
3.0 KiB
Go
123 lines
3.0 KiB
Go
package common
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// ResolveClientIP returns the TCP peer address unless that peer is trusted.
|
|
// Forwarded client address headers are never used for untrusted peers.
|
|
func ResolveClientIP(c *gin.Context, trustedProxies string) string {
|
|
remoteAddr := GetRealClientIP(c)
|
|
remoteIP := net.ParseIP(remoteAddr)
|
|
if remoteIP == nil {
|
|
return remoteAddr
|
|
}
|
|
|
|
proxies, err := parseTrustedProxies(trustedProxies)
|
|
if err != nil || !isIPInNetworks(remoteIP, proxies) {
|
|
return remoteAddr
|
|
}
|
|
|
|
forwardedFor := strings.Join(c.Request.Header.Values("X-Forwarded-For"), ",")
|
|
if strings.TrimSpace(forwardedFor) != "" {
|
|
clientIP, ok := clientIPFromForwardedFor(forwardedFor, proxies)
|
|
if !ok {
|
|
return remoteAddr
|
|
}
|
|
return clientIP
|
|
}
|
|
|
|
realIPValue := strings.Join(c.Request.Header.Values("X-Real-IP"), ",")
|
|
realIP := net.ParseIP(strings.TrimSpace(realIPValue))
|
|
if realIP == nil {
|
|
return remoteAddr
|
|
}
|
|
return realIP.String()
|
|
}
|
|
|
|
func NormalizeTrustedProxies(value string) (string, error) {
|
|
lines := strings.Split(value, "\n")
|
|
normalized := make([]string, 0, len(lines))
|
|
for _, line := range lines {
|
|
item := strings.TrimSpace(line)
|
|
if item == "" {
|
|
continue
|
|
}
|
|
if ip := net.ParseIP(item); ip != nil {
|
|
normalized = append(normalized, ip.String())
|
|
continue
|
|
}
|
|
_, ipNet, err := net.ParseCIDR(item)
|
|
if err != nil {
|
|
return "", fmt.Errorf("invalid trusted proxy entry %q: %w", item, err)
|
|
}
|
|
ones, _ := ipNet.Mask.Size()
|
|
if ones == 0 {
|
|
return "", fmt.Errorf("invalid trusted proxy entry %q: unrestricted CIDR is not allowed", item)
|
|
}
|
|
normalized = append(normalized, ipNet.String())
|
|
}
|
|
return strings.Join(normalized, "\n"), nil
|
|
}
|
|
|
|
func parseTrustedProxies(value string) ([]*net.IPNet, error) {
|
|
normalized, err := NormalizeTrustedProxies(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if normalized == "" {
|
|
return []*net.IPNet{}, nil
|
|
}
|
|
|
|
lines := strings.Split(normalized, "\n")
|
|
proxies := make([]*net.IPNet, 0, len(lines))
|
|
for _, item := range lines {
|
|
if ip := net.ParseIP(item); ip != nil {
|
|
bits := 128
|
|
if ip.To4() != nil {
|
|
bits = 32
|
|
ip = ip.To4()
|
|
}
|
|
proxies = append(proxies, &net.IPNet{IP: ip, Mask: net.CIDRMask(bits, bits)})
|
|
continue
|
|
}
|
|
_, ipNet, err := net.ParseCIDR(item)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
proxies = append(proxies, ipNet)
|
|
}
|
|
return proxies, nil
|
|
}
|
|
|
|
func clientIPFromForwardedFor(value string, trustedProxies []*net.IPNet) (string, bool) {
|
|
items := strings.Split(value, ",")
|
|
ips := make([]net.IP, len(items))
|
|
for i, item := range items {
|
|
ip := net.ParseIP(strings.TrimSpace(item))
|
|
if ip == nil {
|
|
return "", false
|
|
}
|
|
ips[i] = ip
|
|
}
|
|
for i := len(ips) - 1; i >= 0; i-- {
|
|
if i == 0 || !isIPInNetworks(ips[i], trustedProxies) {
|
|
return ips[i].String(), true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
func isIPInNetworks(ip net.IP, networks []*net.IPNet) bool {
|
|
for _, network := range networks {
|
|
if network.Contains(ip) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|