fix(http): set TrustedPlatform=Cloudflare so c.ClientIP reads CF-Connecting-IP

Previous SetTrustedProxies commit (407dbb7) was necessary but
insufficient. In production Manager sits behind Cloudflare in
proxy mode, which:
  - strips the inbound X-Forwarded-For header
  - sets CF-Connecting-IP with the real client IP

Gin's default ClientIP() only knows about X-Forwarded-For + X-Real-IP
— it does NOT recognize CF-Connecting-IP. So every request showed the
docker bridge peer (10.2.3.4) in audit fields and rate-limit buckets
even after we added private ranges to TrustedProxies.

Setting TrustedPlatform = gin.PlatformCloudflare instructs Gin to
read CF-Connecting-IP as ground truth, bypassing the XFF parser.
When the header is absent (health checks, direct non-CF probes)
Gin falls back through TrustedProxies → XFF → RemoteAddr as before.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-21 19:56:11 +08:00
co-authored by Claude Opus 4.7
parent 407dbb7200
commit b48be15d8b
+15
View File
@@ -188,6 +188,21 @@ func main() {
common.FatalLog(fmt.Sprintf("SetTrustedProxies: %v", err))
}
// Cloudflare-aware ClientIP resolution. In production Manager runs
// behind Cloudflare's proxy mode, which strips the inbound
// X-Forwarded-For and replaces it with CF-Connecting-IP carrying
// the real client IP. Setting TrustedPlatform short-circuits Gin's
// default XFF parsing and reads CF-Connecting-IP as ground truth —
// without this, every request shows the docker bridge peer
// (10.2.3.x) regardless of how many CIDRs we add to TrustedProxies.
//
// Honoured globally: c.ClientIP() returns the real IP for all
// downstream consumers (token IP allowlist, rate-limit, device
// last-seen IP, gin access log, audit logs). When the header is
// absent (e.g. health checks from the VM itself, or direct
// non-CF hits) Gin falls back to TrustedProxies → XFF → RemoteAddr.
server.TrustedPlatform = gin.PlatformCloudflare
server.Use(gin.CustomRecovery(func(c *gin.Context, err any) {
common.SysLog(fmt.Sprintf("panic detected: %v", err))
c.JSON(http.StatusInternalServerError, gin.H{