Files
1Panel/core/utils/common/trusted_proxy.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
}