Files
netbird/proxy/internal/accesslog/requestip.go
2026-01-26 09:28:46 +00:00

44 lines
1.6 KiB
Go

package accesslog
import (
"net"
"net/http"
"slices"
"strings"
)
// requestIP attempts to extract the source IP from a request.
// Adapted from https://husobee.github.io/golang/ip-address/2015/12/17/remote-ip-go.html
// with the addition of some newer stdlib functions that are now
// available.
// The concept here is to look backwards through IP headers until
// the first public IP address is found. The hypothesis is that
// even if there are multiple IP addresses specified in these headers,
// the last public IP should be the hop immediately before reaching
// the server and therefore represents the "true" source IP regardless
// of the number of intermediate proxies or network hops.
func extractSourceIP(r *http.Request) string {
for _, h := range []string{"X-Forwarded-For", "X-Real-IP"} {
addresses := strings.Split(r.Header.Get(h), ",")
// Iterate from right to left until we get a public address
// that should be the address right before our proxy.
for _, address := range slices.Backward(addresses) {
// Trim the address because sometimes clients put whitespace in there.
ip := strings.TrimSpace(address)
// Parse the IP so that we can easily check whether it is a valid public address.
realIP := net.ParseIP(ip)
if !realIP.IsGlobalUnicast() || realIP.IsPrivate() || realIP.IsLoopback() {
continue
}
return ip
}
}
// Fallback to the requests RemoteAddr, this is least likely to be correct but
// should at least yield something in the event that the above has failed.
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
ip = r.RemoteAddr
}
return ip
}