From 072025be5437c4e40386affd10309983ce72572c Mon Sep 17 00:00:00 2001 From: benya Date: Thu, 4 Jun 2026 19:39:53 +0300 Subject: [PATCH] Sanitize LuCI upstream proxy headers --- server/internal/httpapi/server.go | 9 +++++++++ server/internal/httpapi/server_test.go | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/server/internal/httpapi/server.go b/server/internal/httpapi/server.go index e08d579..5efa1c8 100644 --- a/server/internal/httpapi/server.go +++ b/server/internal/httpapi/server.go @@ -1122,6 +1122,15 @@ func (a *App) proxyLuCI(w http.ResponseWriter, r *http.Request, deviceID, sessio req.Host = "127.0.0.1" req.Header.Set("Host", "127.0.0.1") req.Header.Del("Accept-Encoding") + req.Header.Del("Forwarded") + req.Header.Del("X-Forwarded-Host") + req.Header.Del("X-Forwarded-Port") + req.Header.Del("X-Forwarded-Proto") + req.Header.Del("X-Real-IP") + req.Header.Del("Origin") + req.Header.Del("Referer") + // A nil slice tells ReverseProxy not to append the operator address. + req.Header["X-Forwarded-For"] = nil removeCookie(req, operatorSessionCookie) removeCookie(req, luciRouteCookie) } diff --git a/server/internal/httpapi/server_test.go b/server/internal/httpapi/server_test.go index 3b3fc64..326469e 100644 --- a/server/internal/httpapi/server_test.go +++ b/server/internal/httpapi/server_test.go @@ -412,6 +412,12 @@ func TestLuCIProxyRequiresActiveSessionAndRewritesPaths(t *testing.T) { http.Error(w, "rejected host", http.StatusForbidden) return } + for _, header := range []string{"Forwarded", "X-Forwarded-For", "X-Forwarded-Host", "X-Forwarded-Port", "X-Forwarded-Proto", "X-Real-IP", "Origin", "Referer"} { + if r.Header.Get(header) != "" { + http.Error(w, "rejected forwarded request", http.StatusForbidden) + return + } + } if strings.Contains(r.Header.Get("Cookie"), "rmm_operator_session=") { t.Fatal("operator session cookie leaked to LuCI upstream") } @@ -478,6 +484,14 @@ func TestLuCIProxyRequiresActiveSessionAndRewritesPaths(t *testing.T) { t.Fatal(err) } req.Header.Set("Authorization", "Bearer operator-test") + req.Header.Set("Forwarded", "for=203.0.113.10;proto=https") + req.Header.Set("X-Forwarded-For", "203.0.113.10") + req.Header.Set("X-Forwarded-Host", "rmm.example.test") + req.Header.Set("X-Forwarded-Port", "443") + req.Header.Set("X-Forwarded-Proto", "https") + req.Header.Set("X-Real-IP", "203.0.113.10") + req.Header.Set("Origin", "https://rmm.example.test") + req.Header.Set("Referer", "https://rmm.example.test/luci/") resp, err := client.Do(req) if err != nil { t.Fatal(err)