fix: user location fix
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RealIP rewrites r.RemoteAddr to the genuine client IP for a deployment where
|
||||||
|
// a SINGLE trusted reverse proxy is the edge and nothing sits in front of it
|
||||||
|
// (here: Dokploy's Traefik — DNS points straight at it).
|
||||||
|
//
|
||||||
|
// Such an edge APPENDS the TCP peer it actually saw to the right of
|
||||||
|
// X-Forwarded-For, so the LAST entry is the real client and can't be spoofed:
|
||||||
|
// a client may only prepend left-hand entries, which we ignore. This is the
|
||||||
|
// opposite of chi's RealIP, which trusts the left-most X-Forwarded-For entry
|
||||||
|
// (and X-Real-IP) and is therefore spoofable behind an appending proxy —
|
||||||
|
// yielding the wrong GeoIP country and a forgeable rate-limit key.
|
||||||
|
//
|
||||||
|
// Downstream consumers (geoip.ClientIP, the rate limiter, the request logger)
|
||||||
|
// all read r.RemoteAddr, so fixing it here fixes them all. When no forwarded
|
||||||
|
// header is present (e.g. a direct hit on the container), RemoteAddr is left
|
||||||
|
// untouched.
|
||||||
|
func RealIP(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if ip := clientIPFromEdge(r); ip != "" {
|
||||||
|
r.RemoteAddr = ip
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func clientIPFromEdge(r *http.Request) string {
|
||||||
|
// Right-most X-Forwarded-For entry = the IP the trusted edge observed.
|
||||||
|
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
|
||||||
|
parts := strings.Split(xff, ",")
|
||||||
|
last := strings.TrimSpace(parts[len(parts)-1])
|
||||||
|
if net.ParseIP(last) != nil {
|
||||||
|
return last
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback: the edge also overwrites X-Real-IP from the connection.
|
||||||
|
if xr := strings.TrimSpace(r.Header.Get("X-Real-Ip")); net.ParseIP(xr) != nil {
|
||||||
|
return xr
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
@@ -407,7 +407,10 @@ func (s *Server) setupRouter() {
|
|||||||
|
|
||||||
// Global middleware
|
// Global middleware
|
||||||
r.Use(chiMiddleware.RequestID)
|
r.Use(chiMiddleware.RequestID)
|
||||||
r.Use(chiMiddleware.RealIP)
|
// Our RealIP (not chi's): a single trusted edge proxy (Dokploy's Traefik)
|
||||||
|
// appends the real client to the right of X-Forwarded-For, so we read the
|
||||||
|
// right-most entry. chi's RealIP trusts the spoofable left-most entry.
|
||||||
|
r.Use(middleware.RealIP)
|
||||||
r.Use(middleware.Logger(s.logger))
|
r.Use(middleware.Logger(s.logger))
|
||||||
r.Use(chiMiddleware.Recoverer)
|
r.Use(chiMiddleware.Recoverer)
|
||||||
r.Use(chiMiddleware.Compress(5))
|
r.Use(chiMiddleware.Compress(5))
|
||||||
|
|||||||
+3
-3
@@ -32,9 +32,9 @@ func CountryFromCloudflare(r *http.Request) string {
|
|||||||
return cc
|
return cc
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientIP returns the best guess at the caller's IP. The chi RealIP
|
// ClientIP returns the best guess at the caller's IP. The middleware.RealIP
|
||||||
// middleware already rewrites r.RemoteAddr from X-Forwarded-For /
|
// middleware already rewrites r.RemoteAddr to the real client (right-most
|
||||||
// X-Real-IP, so we just strip the port.
|
// X-Forwarded-For entry from the trusted edge proxy), so we just strip any port.
|
||||||
func ClientIP(r *http.Request) string {
|
func ClientIP(r *http.Request) string {
|
||||||
host, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr))
|
host, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user