Очередь job получила lease и fencing, телеметрия и прогресс deploy сохраняются, ревизия пишет manifest входов. Обновление префиксов не переписывает неизменённый снимок, большие наборы идут через COPY, а устаревшая ревизия не попадает в deploy. Бандл ноды кэшируется, prune и постановка job больше не держат горячий путь. Co-authored-by: Cursor <cursoragent@cursor.com>
92 lines
2.1 KiB
Go
92 lines
2.1 KiB
Go
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()
|
|
}
|