feat(backend): trust reverse-proxy X-Forwarded-For for the client IP
The auth rate limiter keys on c.ClientIP(), but the router was built with gin.New() and never called SetTrustedProxies — so Gin trusted all proxies by default. Behind a host reverse proxy that meant the limiter either bucketed every request under the proxy's IP, or (with the port reachable directly) could be bypassed by a forged X-Forwarded-For. NewRouter now takes a trusted-proxy list and configures SetTrustedProxies, returning an error on an invalid list so misconfiguration fails fast at startup. The list comes from a new TRUSTED_PROXIES config (CSV of CIDRs/IPs), defaulting to loopback plus the Docker bridge ranges a host proxy reaches the container through. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -32,10 +33,20 @@ func NewRouter(
|
||||
aclHandler *ACLHandler,
|
||||
auditHandler *AuditHandler,
|
||||
staticDir string,
|
||||
) *gin.Engine {
|
||||
trustedProxies []string,
|
||||
) (*gin.Engine, error) {
|
||||
r := gin.New()
|
||||
r.Use(gin.Logger(), gin.Recovery(), securityHeaders())
|
||||
|
||||
// Behind a reverse proxy the client's real IP arrives in X-Forwarded-For.
|
||||
// Trust only the proxy hop(s) so c.ClientIP() — used by the auth rate
|
||||
// limiter — reflects the real client and can't be spoofed by a forged
|
||||
// header from a direct caller. An empty list trusts no proxy (ClientIP is
|
||||
// the immediate peer).
|
||||
if err := r.SetTrustedProxies(trustedProxies); err != nil {
|
||||
return nil, fmt.Errorf("configure trusted proxies: %w", err)
|
||||
}
|
||||
|
||||
// Health check — no auth required.
|
||||
r.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
@@ -189,5 +200,5 @@ func NewRouter(
|
||||
r.NoRoute(spaHandler(staticDir))
|
||||
}
|
||||
|
||||
return r
|
||||
return r, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user