| 1 | package policy |
| 2 | |
| 3 | import ( |
| 4 | "net" |
| 5 | "net/http" |
| 6 | "strings" |
| 7 | ) |
| 8 | |
| 9 | var defaultTrustedProxyCIDRs = mustParseTrustedProxyCIDRs( |
| 10 | "127.0.0.0/8", |
| 11 | "10.0.0.0/8", |
| 12 | "172.16.0.0/12", |
| 13 | "192.168.0.0/16", |
| 14 | "169.254.0.0/16", |
| 15 | "100.64.0.0/10", |
| 16 | "::1/128", |
| 17 | "fc00::/7", |
| 18 | "fe80::/10", |
| 19 | ) |
| 20 | |
| 21 | func isTrustedProxyRemoteAddr(remoteAddr string, trustedProxyCIDRs []*net.IPNet) bool { |
| 22 | remoteIP := parseRemoteAddrIP(remoteAddr) |
| 23 | if remoteIP == nil { |
| 24 | return false |
| 25 | } |
| 26 | |
| 27 | networks := trustedProxyCIDRs |
| 28 | if len(networks) == 0 { |
| 29 | networks = defaultTrustedProxyCIDRs |
| 30 | } |
| 31 | for _, network := range networks { |
| 32 | if network != nil && network.Contains(remoteIP) { |
| 33 | return true |
| 34 | } |
| 35 | } |
| 36 | return false |
| 37 | } |
| 38 | |
| 39 | func (r *Runtime) ExtractClientIP(req *http.Request) string { |
| 40 | if r == nil { |
| 41 | return "" |
| 42 | } |
| 43 | if req == nil { |
| 44 | return "" |
| 45 | } |
| 46 | |
| 47 | cfg := runtimeConfig{} |
| 48 | if r.config != nil { |
| 49 | cfg = r.config.Load() |
| 50 | } |
| 51 | if cfg.trustProxyHeaders && isTrustedProxyRemoteAddr(req.RemoteAddr, cfg.trustedProxyCIDRs) { |
| 52 | if xff := req.Header.Get("X-Forwarded-For"); xff != "" { |
| 53 | if before, _, ok := strings.Cut(xff, ","); ok { |
| 54 | if ip := normalizeClientIPCandidate(before); ip != "" { |
| 55 | return ip |
| 56 | } |
| 57 | } else if ip := normalizeClientIPCandidate(xff); ip != "" { |
| 58 | return ip |
| 59 | } |
| 60 | } |
| 61 | if xri := req.Header.Get("X-Real-IP"); xri != "" { |
| 62 | if ip := normalizeClientIPCandidate(xri); ip != "" { |
| 63 | return ip |
| 64 | } |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | host, _, err := net.SplitHostPort(req.RemoteAddr) |
| 69 | if err != nil { |
| 70 | return strings.TrimSpace(req.RemoteAddr) |
| 71 | } |
| 72 | if normalized := normalizeClientIPCandidate(host); normalized != "" { |
| 73 | return normalized |
| 74 | } |
| 75 | return strings.TrimSpace(host) |
| 76 | } |
| 77 | |
| 78 | func parseRemoteAddrIP(remoteAddr string) net.IP { |
| 79 | remoteAddr = strings.TrimSpace(remoteAddr) |
| 80 | if remoteAddr == "" { |
| 81 | return nil |
| 82 | } |
| 83 | host := remoteAddr |
| 84 | if parsedHost, _, err := net.SplitHostPort(remoteAddr); err == nil { |
| 85 | host = parsedHost |
| 86 | } |
| 87 | return net.ParseIP(strings.TrimSpace(host)) |
| 88 | } |
| 89 | |
| 90 | func mustParseTrustedProxyCIDRs(values ...string) []*net.IPNet { |
| 91 | cidrs := make([]*net.IPNet, 0, len(values)) |
| 92 | for _, value := range values { |
| 93 | _, network, err := net.ParseCIDR(value) |
| 94 | if err != nil { |
| 95 | panic(err) |
| 96 | } |
| 97 | cidrs = append(cidrs, network) |
| 98 | } |
| 99 | return cidrs |
| 100 | } |
| 101 | |
| 102 | func normalizeClientIPCandidate(raw string) string { |
| 103 | candidate := strings.TrimSpace(raw) |
| 104 | if candidate == "" { |
| 105 | return "" |
| 106 | } |
| 107 | if ip := net.ParseIP(candidate); ip != nil { |
| 108 | return candidate |
| 109 | } |
| 110 | host, _, err := net.SplitHostPort(candidate) |
| 111 | if err != nil { |
| 112 | return "" |
| 113 | } |
| 114 | host = strings.TrimSpace(host) |
| 115 | if host == "" || net.ParseIP(host) == nil { |
| 116 | return "" |
| 117 | } |
| 118 | return host |
| 119 | } |