From a498dd69915a006c07e9d82660a2185d7e8136ee Mon Sep 17 00:00:00 2001 From: kolaente Date: Fri, 20 Mar 2026 10:07:04 +0100 Subject: [PATCH] fix: configure Echo IPExtractor to prevent rate limit bypass via spoofed headers --- pkg/routes/routes.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/pkg/routes/routes.go b/pkg/routes/routes.go index d51b78346..02c2736f7 100644 --- a/pkg/routes/routes.go +++ b/pkg/routes/routes.go @@ -54,6 +54,7 @@ package routes import ( "context" "log/slog" + "net" "strings" "time" @@ -125,6 +126,24 @@ func NewEcho() *echo.Echo { }), }) + // Configure IP extraction to prevent rate limit bypass via spoofed headers. + // Echo's default RealIP() trusts X-Forwarded-For and X-Real-IP unconditionally, + // which allows attackers to bypass IP-based rate limits. + // See: https://echo.labstack.com/docs/ip-address + switch config.ServiceIPExtractionMethod.GetString() { + case "xff": + trustOptions := parseTrustedProxies(config.ServiceTrustedProxies.GetString()) + e.IPExtractor = echo.ExtractIPFromXFFHeader(trustOptions...) + log.Debugf("IP extraction: X-Forwarded-For with %d trusted proxy ranges", len(trustOptions)) + case "realip": + trustOptions := parseTrustedProxies(config.ServiceTrustedProxies.GetString()) + e.IPExtractor = echo.ExtractIPFromRealIPHeader(trustOptions...) + log.Debugf("IP extraction: X-Real-IP with %d trusted proxy ranges", len(trustOptions)) + default: + e.IPExtractor = echo.ExtractIPDirect() + log.Debugf("IP extraction: direct (TCP remote address)") + } + e.Logger = log.NewEchoLogger(config.LogEnabled.GetBool(), config.LogHTTP.GetString(), config.LogFormat.GetString()) // Logger @@ -181,6 +200,27 @@ func NewEcho() *echo.Echo { return e } +func parseTrustedProxies(proxies string) []echo.TrustOption { + if proxies == "" { + return nil + } + + var options []echo.TrustOption + for _, cidr := range strings.Split(proxies, ",") { + cidr = strings.TrimSpace(cidr) + if cidr == "" { + continue + } + _, ipNet, err := net.ParseCIDR(cidr) + if err != nil { + log.Warningf("Invalid trusted proxy CIDR %q: %v", cidr, err) + continue + } + options = append(options, echo.TrustIPRange(ipNet)) + } + return options +} + func setupSentry(e *echo.Echo) { if !config.SentryEnabled.GetBool() { return