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>
This commit is contained in:
Patrick O'Doherty
2025-05-15 14:26:19 -07:00
committed by GitHub
parent 824985afe1
commit 336b3b7df0
2 changed files with 91 additions and 9 deletions

View File

@@ -53,7 +53,7 @@ import (
"strings"
"time"
"tailscale.com/client/local"
"tailscale.com/client/tailscale/apitype"
"tailscale.com/tailcfg"
"tailscale.com/tsnet"
)
@@ -195,13 +195,7 @@ func main() {
log.Fatal(http.Serve(ln, proxy))
}
func modifyRequest(req *http.Request, localClient *local.Client) {
// with enable_login_token set to true, we get a cookie that handles
// auth for paths that are not /login
if req.URL.Path != "/login" {
return
}
func modifyRequest(req *http.Request, localClient whoisIdentitySource) {
// Delete any existing X-Webauth-* headers to prevent possible spoofing
// if getting Tailnet identity fails.
for h := range req.Header {
@@ -210,6 +204,13 @@ func modifyRequest(req *http.Request, localClient *local.Client) {
}
}
// Set the X-Webauth-* headers only for the /login path
// With enable_login_token set to true, we get a cookie that handles
// auth for paths that are not /login
if req.URL.Path != "/login" {
return
}
user, role, err := getTailscaleIdentity(req.Context(), localClient, req.RemoteAddr)
if err != nil {
log.Printf("error getting Tailscale user: %v", err)
@@ -221,7 +222,7 @@ func modifyRequest(req *http.Request, localClient *local.Client) {
req.Header.Set("X-Webauth-Role", role.String())
}
func getTailscaleIdentity(ctx context.Context, localClient *local.Client, ipPort string) (*tailcfg.UserProfile, grafanaRole, error) {
func getTailscaleIdentity(ctx context.Context, localClient whoisIdentitySource, ipPort string) (*tailcfg.UserProfile, grafanaRole, error) {
whois, err := localClient.WhoIs(ctx, ipPort)
if err != nil {
return nil, ViewerRole, fmt.Errorf("failed to identify remote host: %w", err)
@@ -248,3 +249,7 @@ func getTailscaleIdentity(ctx context.Context, localClient *local.Client, ipPort
return whois.UserProfile, role, nil
}
type whoisIdentitySource interface {
WhoIs(ctx context.Context, ipPort string) (*apitype.WhoIsResponse, error)
}