66 lines
1.3 KiB
Go
66 lines
1.3 KiB
Go
package server
|
|
|
|
import (
|
|
"net"
|
|
"net/http"
|
|
"net/netip"
|
|
"strings"
|
|
)
|
|
|
|
// ClientIP returns the client address for access control, using X-Forwarded-For /
|
|
// X-Real-IP only when the direct peer is in trusted CIDRs.
|
|
func ClientIP(r *http.Request, trusted []netip.Prefix) netip.Addr {
|
|
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
if err != nil {
|
|
host = r.RemoteAddr
|
|
}
|
|
peer, err := netip.ParseAddr(host)
|
|
if err != nil {
|
|
return netip.Addr{}
|
|
}
|
|
if !containsIP(trusted, peer) {
|
|
return peer
|
|
}
|
|
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
|
|
parts := strings.Split(xff, ",")
|
|
for _, p := range parts {
|
|
p = strings.TrimSpace(p)
|
|
if p == "" {
|
|
continue
|
|
}
|
|
if a, err := netip.ParseAddr(p); err == nil {
|
|
return a
|
|
}
|
|
}
|
|
}
|
|
if xr := strings.TrimSpace(r.Header.Get("X-Real-IP")); xr != "" {
|
|
if a, err := netip.ParseAddr(xr); err == nil {
|
|
return a
|
|
}
|
|
}
|
|
return peer
|
|
}
|
|
|
|
func containsIP(prefixes []netip.Prefix, addr netip.Addr) bool {
|
|
for _, p := range prefixes {
|
|
if p.Contains(addr) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Allowed reports whether addr matches whitelist rules.
|
|
func Allowed(addr netip.Addr, allowAll bool, whitelist []netip.Prefix) bool {
|
|
if !addr.IsValid() {
|
|
return false
|
|
}
|
|
if allowAll {
|
|
return true
|
|
}
|
|
if len(whitelist) == 0 {
|
|
return false
|
|
}
|
|
return containsIP(whitelist, addr)
|
|
}
|