refactor(10.2-01): nest framework packages under modules
- Move remaining beach packages and embedded admin assets\n- Rewrite framework, example, build, and gate paths
This commit is contained in:
105
modules/surf/clientip.go
Normal file
105
modules/surf/clientip.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package surf
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/compass"
|
||||
)
|
||||
|
||||
// ClientIP is the single source of client IP for limiter keys (D-04).
|
||||
// RemoteAddr is used unless it parses as being inside one of trusted;
|
||||
// in that case the rightmost X-Forwarded-For hop NOT inside any trusted
|
||||
// prefix is used. An empty trusted list means RemoteAddr only.
|
||||
func ClientIP(r *http.Request, trusted []netip.Prefix) string {
|
||||
if r == nil {
|
||||
return ""
|
||||
}
|
||||
remote := parseIP(r.RemoteAddr)
|
||||
if len(trusted) == 0 || remote == (netip.Addr{}) || !addrTrusted(remote, trusted) {
|
||||
if remote == (netip.Addr{}) {
|
||||
return ""
|
||||
}
|
||||
return remote.String()
|
||||
}
|
||||
xff := r.Header.Get("X-Forwarded-For")
|
||||
if xff == "" {
|
||||
return remote.String()
|
||||
}
|
||||
hops := strings.Split(xff, ",")
|
||||
for i := len(hops) - 1; i >= 0; i-- {
|
||||
hop := strings.TrimSpace(hops[i])
|
||||
if hop == "" {
|
||||
continue
|
||||
}
|
||||
addr, err := netip.ParseAddr(hop)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
addr = addr.Unmap()
|
||||
if !addrTrusted(addr, trusted) {
|
||||
return addr.String()
|
||||
}
|
||||
}
|
||||
return remote.String()
|
||||
}
|
||||
|
||||
// TrustedProxies reads http.trusted_proxies (a []string of CIDRs) from cfg
|
||||
// and parses it once into []netip.Prefix. A malformed entry is skipped, not
|
||||
// fatal (logged by the caller if desired).
|
||||
func TrustedProxies(cfg *compass.Config) []netip.Prefix {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
raw, ok := cfg.Lookup("http.trusted_proxies")
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
var entries []string
|
||||
switch v := raw.(type) {
|
||||
case []string:
|
||||
entries = v
|
||||
case []any:
|
||||
for _, item := range v {
|
||||
s, _ := item.(string)
|
||||
if s != "" {
|
||||
entries = append(entries, s)
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
var out []netip.Prefix
|
||||
for _, e := range entries {
|
||||
p, err := netip.ParsePrefix(strings.TrimSpace(e))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, p)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func parseIP(remoteAddr string) netip.Addr {
|
||||
host, _, err := net.SplitHostPort(remoteAddr)
|
||||
if err != nil {
|
||||
host = remoteAddr
|
||||
}
|
||||
host = strings.Trim(host, "[]")
|
||||
addr, err := netip.ParseAddr(host)
|
||||
if err != nil {
|
||||
return netip.Addr{}
|
||||
}
|
||||
return addr.Unmap()
|
||||
}
|
||||
|
||||
func addrTrusted(addr netip.Addr, trusted []netip.Prefix) bool {
|
||||
for _, p := range trusted {
|
||||
if p.Contains(addr) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user