From e035fb07a9918bddc5ddd75f5d58eaced9dc2b5a Mon Sep 17 00:00:00 2001 From: root Date: Sat, 25 Apr 2026 11:45:07 +0800 Subject: [PATCH] fix: trust Cloudflare CF-Connecting-IP for IP extraction When behind Cloudflare CDN, RemoteAddr shows CF's IP, breaking rate limiting and logging. Trust CF-Connecting-IP (set by CF, cannot be spoofed by clients) and fall back to RemoteAddr for direct connections. --- web/controller/util.go | 12 ++++++++---- web/middleware/ratelimit.go | 8 ++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/web/controller/util.go b/web/controller/util.go index f1330b9d..1ca70630 100644 --- a/web/controller/util.go +++ b/web/controller/util.go @@ -12,11 +12,15 @@ import ( "github.com/gin-gonic/gin" ) -// getRemoteIp extracts the real IP address from the direct connection. -// Uses RemoteAddr to prevent IP spoofing via X-Real-IP/X-Forwarded-For headers. -// If the panel is behind a trusted reverse proxy, configure Gin's SetTrustedProxies -// to re-enable header-based IP detection. +// getRemoteIp extracts the real IP address from the request. +// Trusts Cloudflare's CF-Connecting-IP header (overwritten by CF, cannot be spoofed by clients). +// Falls back to RemoteAddr for direct connections without a trusted proxy. func getRemoteIp(c *gin.Context) string { + // Cloudflare CDN sets CF-Connecting-IP to the real client IP and overwrites it, + // so it can be trusted even though it's a header. + if cfIP := c.GetHeader("CF-Connecting-IP"); cfIP != "" { + return cfIP + } addr := c.Request.RemoteAddr ip, _, _ := net.SplitHostPort(addr) return ip diff --git a/web/middleware/ratelimit.go b/web/middleware/ratelimit.go index a6a26973..bf3960a3 100644 --- a/web/middleware/ratelimit.go +++ b/web/middleware/ratelimit.go @@ -37,8 +37,12 @@ func RateLimitMiddleware(maxRequests int, window time.Duration) gin.HandlerFunc }() return func(c *gin.Context) { - // Use RemoteAddr directly to prevent IP spoofing via headers - ip := c.Request.RemoteAddr + // Trust Cloudflare's CF-Connecting-IP (overwritten by CF, not spoofable). + // Fall back to RemoteAddr for non-CDN deployments. + ip := c.GetHeader("CF-Connecting-IP") + if ip == "" { + ip = c.Request.RemoteAddr + } mu.Lock() now := time.Now()