Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 3 additions & 22 deletions dashboard/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,17 +422,10 @@ func readSmallBody(r *http.Request, maxBytes int64) (string, error) {
}

// localhostOnly rejects requests not originating from loopback.
// Trusts X-Real-IP only when the direct connection is from localhost.
func localhostOnly(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
remoteIP, _, _ := net.SplitHostPort(r.RemoteAddr)
clientIP := remoteIP
if remoteIP == "127.0.0.1" || remoteIP == "::1" || remoteIP == "localhost" {
if realIP := r.Header.Get("X-Real-IP"); realIP != "" {
clientIP = realIP
}
}
if clientIP != "127.0.0.1" && clientIP != "::1" && clientIP != "localhost" {
if remoteIP != "127.0.0.1" && remoteIP != "::1" && remoteIP != "localhost" {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
Expand Down Expand Up @@ -472,13 +465,7 @@ func (h *Handler) Serve(addr string) error {

mux.HandleFunc("/api/nodes", func(w http.ResponseWriter, r *http.Request) {
remoteIP, _, _ := net.SplitHostPort(r.RemoteAddr)
clientIP := remoteIP
if remoteIP == "127.0.0.1" || remoteIP == "::1" || remoteIP == "localhost" {
if realIP := r.Header.Get("X-Real-IP"); realIP != "" {
clientIP = realIP
}
}
if clientIP != "127.0.0.1" && clientIP != "::1" && clientIP != "localhost" {
if remoteIP != "127.0.0.1" && remoteIP != "::1" && remoteIP != "localhost" {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
Expand Down Expand Up @@ -644,13 +631,7 @@ func (h *Handler) Serve(addr string) error {
// Snapshot trigger endpoint (POST only, localhost only).
mux.HandleFunc("/api/snapshot", func(w http.ResponseWriter, r *http.Request) {
remoteIP, _, _ := net.SplitHostPort(r.RemoteAddr)
clientIP := remoteIP
if remoteIP == "127.0.0.1" || remoteIP == "::1" || remoteIP == "localhost" {
if realIP := r.Header.Get("X-Real-IP"); realIP != "" {
clientIP = realIP
}
}
if clientIP != "127.0.0.1" && clientIP != "::1" && clientIP != "localhost" {
if remoteIP != "127.0.0.1" && remoteIP != "::1" && remoteIP != "localhost" {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
Expand Down
12 changes: 6 additions & 6 deletions dashboard/zz_more_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,20 +262,20 @@ func TestLocalhostOnly_RejectsRemote(t *testing.T) {
}
}

func TestLocalhostOnly_HonorsXRealIPFromLocalhost(t *testing.T) {
func TestLocalhostOnly_IgnoresXRealIPFromLocalhost(t *testing.T) {
t.Parallel()
// When the direct connection is localhost but X-Real-IP is a remote
// address, the middleware must use the X-Real-IP value for the
// allow/deny check (i.e. reject).
// When the direct connection is localhost, the middleware must allow
// regardless of X-Real-IP header (which may be spoofed by a remote
// attacker through a reverse proxy on the same host).
called := false
h := localhostOnly(func(http.ResponseWriter, *http.Request) { called = true })
r := httptest.NewRequest("GET", "/", nil)
r.RemoteAddr = "127.0.0.1:5555"
r.Header.Set("X-Real-IP", "8.8.8.8")
rw := httptest.NewRecorder()
h(rw, r)
if called {
t.Error("X-Real-IP=remote should be blocked even from loopback")
if !called {
t.Error("X-Real-IP should be ignored; loopback request should pass")
}
}

Expand Down
Loading