package httpapi import ( "net" "net/http" "net/netip" "os" "strings" ) // trustedProxyPrefixes returns no trusted peers when any configured CIDR is invalid. // An empty configuration deliberately treats every connection as direct. func trustedProxyPrefixes() []netip.Prefix { raw := strings.TrimSpace(os.Getenv("EVOBGP_TRUSTED_PROXY_CIDRS")) if raw == "" { return nil } parts := strings.Split(raw, ",") prefixes := make([]netip.Prefix, 0, len(parts)) for _, part := range parts { prefix, err := netip.ParsePrefix(strings.TrimSpace(part)) if err != nil { return nil } if prefix.Addr().Is4In6() { if prefix.Bits() < 96 { return nil } prefix = netip.PrefixFrom(prefix.Addr().Unmap(), prefix.Bits()-96) } prefixes = append(prefixes, prefix.Masked()) } return prefixes } func peerAddress(r *http.Request) (netip.Addr, bool) { if r == nil { return netip.Addr{}, false } host, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr)) if err != nil { host = strings.TrimSpace(r.RemoteAddr) } ip, err := netip.ParseAddr(host) if err != nil { return netip.Addr{}, false } return ip.WithZone("").Unmap(), true } func isTrustedProxy(ip netip.Addr, prefixes []netip.Prefix) bool { for _, prefix := range prefixes { if prefix.Contains(ip) || prefix.Contains(ip.Unmap()) { return true } } return false } func clientIPWithTrustedProxies(r *http.Request, prefixes []netip.Prefix) string { peer, ok := peerAddress(r) if !ok { if r == nil { return "" } return strings.TrimSpace(r.RemoteAddr) } if !isTrustedProxy(peer, prefixes) { return peer.String() } values := r.Header.Values("X-Forwarded-For") if len(values) != 1 || strings.TrimSpace(values[0]) == "" { return peer.String() } hops := strings.Split(values[0], ",") current := peer for i := len(hops) - 1; i >= 0; i-- { if !isTrustedProxy(current, prefixes) { return current.String() } next, err := netip.ParseAddr(strings.TrimSpace(hops[i])) if err != nil { return peer.String() } if next.Zone() != "" { return peer.String() } current = next.Unmap() } return current.String() }