Harden HTTP defaults and proxy header trust.

Add explicit server timeout/header limits, require trusted proxy CIDRs when honoring forwarded IP headers, and document single-instance storage expectations.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-31 21:09:00 -05:00
parent dbf112a5b7
commit dbb72da312
7 changed files with 246 additions and 27 deletions
+23 -6
View File
@@ -59,14 +59,14 @@ func (r *RateLimiter) Allow(ip string) bool {
return v.limiter.Allow()
}
func AllowlistMiddleware(allowed []netip.Prefix, trustProxyHeaders bool, logger *log.Logger) func(http.Handler) http.Handler {
func AllowlistMiddleware(allowed []netip.Prefix, trustProxyHeaders bool, trustedProxies []netip.Prefix, logger *log.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
if len(allowed) == 0 {
return next
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
clientIP, ok := extractClientIP(r, trustProxyHeaders)
clientIP, ok := extractClientIP(r, trustProxyHeaders, trustedProxies)
if !ok {
http.Error(w, "unable to determine client ip", http.StatusForbidden)
return
@@ -85,10 +85,10 @@ func AllowlistMiddleware(allowed []netip.Prefix, trustProxyHeaders bool, logger
}
}
func RateLimitMiddleware(limiter *RateLimiter, trustProxyHeaders bool) func(http.Handler) http.Handler {
func RateLimitMiddleware(limiter *RateLimiter, trustProxyHeaders bool, trustedProxies []netip.Prefix) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
clientIP, ok := extractClientIP(r, trustProxyHeaders)
clientIP, ok := extractClientIP(r, trustProxyHeaders, trustedProxies)
if !ok {
http.Error(w, "unable to determine client ip", http.StatusTooManyRequests)
return
@@ -115,8 +115,13 @@ func Chain(handler http.Handler, middlewares ...func(http.Handler) http.Handler)
return wrapped
}
func extractClientIP(r *http.Request, trustProxyHeaders bool) (netip.Addr, bool) {
if trustProxyHeaders {
func extractClientIP(r *http.Request, trustProxyHeaders bool, trustedProxies []netip.Prefix) (netip.Addr, bool) {
remoteIP, ok := remoteIPFromRequest(r)
if !ok {
return netip.Addr{}, false
}
if trustProxyHeaders && isTrustedProxy(remoteIP, trustedProxies) {
if xff := strings.TrimSpace(r.Header.Get("X-Forwarded-For")); xff != "" {
parts := strings.Split(xff, ",")
if len(parts) > 0 {
@@ -131,7 +136,10 @@ func extractClientIP(r *http.Request, trustProxyHeaders bool) (netip.Addr, bool)
}
}
}
return remoteIP, true
}
func remoteIPFromRequest(r *http.Request) (netip.Addr, bool) {
host, _, err := net.SplitHostPort(strings.TrimSpace(r.RemoteAddr))
if err != nil {
if ip, parseErr := netip.ParseAddr(strings.TrimSpace(r.RemoteAddr)); parseErr == nil {
@@ -146,6 +154,15 @@ func extractClientIP(r *http.Request, trustProxyHeaders bool) (netip.Addr, bool)
return ip, true
}
func isTrustedProxy(remoteIP netip.Addr, trusted []netip.Prefix) bool {
for _, prefix := range trusted {
if prefix.Contains(remoteIP) {
return true
}
}
return false
}
func writeJSON(w http.ResponseWriter, status int, payload any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)