HTTP reverse proxy for Tailscale

don't set webauth headers for tagged nodes

+28 -4
+12 -2
tsproxy.go
··· 38 38 return 39 39 } 40 40 41 - // TODO(sr) Forbid access to tagged users (i.e. machines)? 41 + if whois.Node == nil { 42 + http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 43 + logger.Error("tailscale whois", slog.String("err", "node missing")) 44 + return 45 + } 46 + 42 47 if whois.UserProfile == nil { 43 48 http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) 44 49 logger.Error("tailscale whois", slog.String("err", "user profile missing")) 45 50 return 46 51 } 47 52 53 + // Proxy requests from tagged nodes as is. 54 + if whois.Node.IsTagged() { 55 + rproxy.ServeHTTP(w, r) 56 + return 57 + } 58 + 48 59 req := r.Clone(r.Context()) 49 60 req.Header.Set("X-Webauth-User", whois.UserProfile.LoginName) 50 61 req.Header.Set("X-Webauth-Name", whois.UserProfile.DisplayName) 51 - 52 62 rproxy.ServeHTTP(w, req) 53 63 }) 54 64 }
+16 -2
tsproxy_test.go
··· 114 114 want: http.StatusInternalServerError, 115 115 }, 116 116 { 117 - name: "tailscale whois ok", 117 + name: "tailscale whois no node", 118 118 whois: func(_ context.Context, _ string) (*apitype.WhoIsResponse, error) { 119 - return &apitype.WhoIsResponse{UserProfile: &tailcfg.UserProfile{LoginName: "login", DisplayName: "name"}}, nil 119 + return &apitype.WhoIsResponse{UserProfile: &tailcfg.UserProfile{LoginName: "login"}}, nil 120 + }, 121 + want: http.StatusInternalServerError, 122 + }, 123 + { 124 + name: "tailscale whois ok (tagged node)", 125 + whois: func(_ context.Context, _ string) (*apitype.WhoIsResponse, error) { 126 + return &apitype.WhoIsResponse{UserProfile: &tailcfg.UserProfile{LoginName: "tagged-devices"}, Node: &tailcfg.Node{Tags: []string{"foo"}}}, nil 127 + }, 128 + want: http.StatusOK, 129 + }, 130 + { 131 + name: "tailscale whois ok (user)", 132 + whois: func(_ context.Context, _ string) (*apitype.WhoIsResponse, error) { 133 + return &apitype.WhoIsResponse{UserProfile: &tailcfg.UserProfile{LoginName: "login", DisplayName: "name"}, Node: &tailcfg.Node{Name: "login.ts.net"}}, nil 120 134 }, 121 135 want: http.StatusOK, 122 136 wantHeaders: map[string]string{