Ctrl K
ComunidadGuías…

internal/helpers/client_ip.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package helpers

import (
	"net"
	"net/http"
	"net/netip"
	"os"
	"strconv"
	"strings"
)

// ClientIP resolves the caller's address for IP-based access restrictions.
//
// By default it is the TCP peer (RemoteAddr): unforgeable, but behind a load
// balancer every request carries the balancer's address. When
// TRUST_PROXY_HEADERS=true the client is read from X-Forwarded-For instead.
//
// X-Forwarded-For is a comma-separated list to which every proxy appends the
// address it received the connection from, so the entries a client can forge
// always sit to the LEFT of the one your outermost trusted proxy added. We
// therefore count trusted proxies from the RIGHT: with N proxies in front of
// the server, the real client is the entry N positions from the end.
// TRUST_PROXY_DEPTH sets N (default 1, the single load-balancer case). Counting
// from the right keeps the choice unspoofable, because extra entries a client
// injects only grow the left of the list and never shift the Nth-from-right
// entry.
//
// If the header carries fewer entries than TRUST_PROXY_DEPTH, the request did
// not traverse the whole trusted chain, so we fall back to RemoteAddr (the
// immediate peer), which a client allowlist will reject. Returns the zero Addr
// when nothing parses; callers with an IP allowlist must treat an unresolvable
// address as a mismatch, not as a pass.
//
// The env vars are read with os.Getenv rather than config.GetEnv because the
// config package imports helpers (import cycle); neither var has a config
// default, so the behavior is identical.
func ClientIP(r *http.Request) netip.Addr {
	if os.Getenv("TRUST_PROXY_HEADERS") == "true" {
		forwardedFor := r.Header.Get("X-Forwarded-For")
		if forwardedFor != "" {
			entries := strings.Split(forwardedFor, ",")
			if idx := len(entries) - trustedProxyDepth(); idx >= 0 {
				candidate := strings.TrimSpace(entries[idx])
				if addr, err := netip.ParseAddr(candidate); err == nil {
					return addr.Unmap()
				}
			}
		}
	}
	host, _, err := net.SplitHostPort(r.RemoteAddr)
	if err != nil {
		host = r.RemoteAddr
	}
	addr, err := netip.ParseAddr(host)
	if err != nil {
		return netip.Addr{}
	}
	return addr.Unmap()
}

// trustedProxyDepth is the number of trusted proxies in front of the server,
// from TRUST_PROXY_DEPTH. It defaults to 1 and is never below 1: a value of 0
// or less, or an unparseable one, would mean "trust the rightmost entry, which
// a client can influence in a multi-proxy chain", so it is clamped up to 1.
func trustedProxyDepth() int {
	if raw := os.Getenv("TRUST_PROXY_DEPTH"); raw != "" {
		if depth, err := strconv.Atoi(strings.TrimSpace(raw)); err == nil && depth >= 1 {
			return depth
		}
	}
	return 1
}