cmd/proxy-to-grafana: strip X-Webauth* headers from all requests (#15985)
Update proxy-to-grafana to strip any X-Webauth prefixed headers passed by the client in *every* request, not just those to /login. /api/ routes will also accept these headers to authenticate users, necessitating their removal to prevent forgery. Updates tailscale/corp#28687 Signed-off-by: Patrick O'Doherty <patrick@tailscale.com>main
parent
824985afe1
commit
336b3b7df0
@ -0,0 +1,77 @@ |
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
package main |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"net/http/httptest" |
||||
"testing" |
||||
|
||||
"tailscale.com/client/tailscale/apitype" |
||||
"tailscale.com/tailcfg" |
||||
) |
||||
|
||||
type mockWhoisSource struct { |
||||
id *apitype.WhoIsResponse |
||||
} |
||||
|
||||
func (m *mockWhoisSource) WhoIs(ctx context.Context, remoteAddr string) (*apitype.WhoIsResponse, error) { |
||||
if m.id == nil { |
||||
return nil, fmt.Errorf("missing mock identity") |
||||
} |
||||
return m.id, nil |
||||
} |
||||
|
||||
var whois = &apitype.WhoIsResponse{ |
||||
UserProfile: &tailcfg.UserProfile{ |
||||
LoginName: "foobar@example.com", |
||||
DisplayName: "Foobar", |
||||
}, |
||||
Node: &tailcfg.Node{ |
||||
ID: 1, |
||||
}, |
||||
} |
||||
|
||||
func TestModifyRequest_Login(t *testing.T) { |
||||
req := httptest.NewRequest("GET", "/login", nil) |
||||
modifyRequest(req, &mockWhoisSource{id: whois}) |
||||
|
||||
if got := req.Header.Get("X-Webauth-User"); got != "foobar@example.com" { |
||||
t.Errorf("X-Webauth-User = %q; want %q", got, "foobar@example.com") |
||||
} |
||||
|
||||
if got := req.Header.Get("X-Webauth-Role"); got != "Viewer" { |
||||
t.Errorf("X-Webauth-Role = %q; want %q", got, "Viewer") |
||||
} |
||||
} |
||||
|
||||
func TestModifyRequest_RemoveHeaders_Login(t *testing.T) { |
||||
req := httptest.NewRequest("GET", "/login", nil) |
||||
req.Header.Set("X-Webauth-User", "malicious@example.com") |
||||
req.Header.Set("X-Webauth-Role", "Admin") |
||||
|
||||
modifyRequest(req, &mockWhoisSource{id: whois}) |
||||
|
||||
if got := req.Header.Get("X-Webauth-User"); got != "foobar@example.com" { |
||||
t.Errorf("X-Webauth-User = %q; want %q", got, "foobar@example.com") |
||||
} |
||||
if got := req.Header.Get("X-Webauth-Role"); got != "Viewer" { |
||||
t.Errorf("X-Webauth-Role = %q; want %q", got, "Viewer") |
||||
} |
||||
} |
||||
|
||||
func TestModifyRequest_RemoveHeaders_API(t *testing.T) { |
||||
req := httptest.NewRequest("DELETE", "/api/org/users/1", nil) |
||||
req.Header.Set("X-Webauth-User", "malicious@example.com") |
||||
req.Header.Set("X-Webauth-Role", "Admin") |
||||
|
||||
modifyRequest(req, &mockWhoisSource{id: whois}) |
||||
|
||||
if got := req.Header.Get("X-Webauth-User"); got != "" { |
||||
t.Errorf("X-Webauth-User = %q; want %q", got, "") |
||||
} |
||||
if got := req.Header.Get("X-Webauth-Role"); got != "" { |
||||
t.Errorf("X-Webauth-Role = %q; want %q", got, "") |
||||
} |
||||
} |
||||
Loading…
Reference in new issue