ipn/localapi: add debug-web-client endpoint

Debug endpoint for the web client's auth flow to talk back to the
control server. Restricted behind a feature flag on control.

We will either be removing this debug endpoint, or renaming it
before launching the web client updates.

Updates tailscale/corp#14335

Signed-off-by: Sonia Appasamy <sonia@tailscale.com>
This commit is contained in:
Sonia Appasamy 2023-10-16 10:32:33 -04:00 committed by Sonia Appasamy
parent e06f2f1873
commit feabb34ea0

View File

@ -79,6 +79,7 @@
"debug-peer-endpoint-changes": (*Handler).serveDebugPeerEndpointChanges, "debug-peer-endpoint-changes": (*Handler).serveDebugPeerEndpointChanges,
"debug-capture": (*Handler).serveDebugCapture, "debug-capture": (*Handler).serveDebugCapture,
"debug-log": (*Handler).serveDebugLog, "debug-log": (*Handler).serveDebugLog,
"debug-web-client": (*Handler).serveDebugWebClient,
"derpmap": (*Handler).serveDERPMap, "derpmap": (*Handler).serveDERPMap,
"dev-set-state-store": (*Handler).serveDevSetStateStore, "dev-set-state-store": (*Handler).serveDevSetStateStore,
"set-push-device-token": (*Handler).serveSetPushDeviceToken, "set-push-device-token": (*Handler).serveSetPushDeviceToken,
@ -2055,6 +2056,65 @@ func (h *Handler) serveQueryFeature(w http.ResponseWriter, r *http.Request) {
} }
} }
// serveDebugWebClient is for use by the web client to communicate with
// the control server for browser auth sessions.
//
// This is an unsupported localapi endpoint and restricted to flagged
// domains on the control side. TODO(tailscale/#14335): Rename this handler.
func (h *Handler) serveDebugWebClient(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite {
http.Error(w, "access denied", http.StatusForbidden)
return
}
if r.Method != "POST" {
http.Error(w, "POST required", http.StatusMethodNotAllowed)
return
}
type reqData struct {
ID string
Src tailcfg.NodeID
}
var data reqData
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
http.Error(w, "invalid JSON body", 400)
return
}
nm := h.b.NetMap()
if nm == nil || !nm.SelfNode.Valid() {
http.Error(w, "[unexpected] no self node", 400)
return
}
dst := nm.SelfNode.ID()
var noiseURL string
if data.ID != "" {
noiseURL = fmt.Sprintf("https://unused/machine/webclient/wait/%d/to/%d/%s", data.Src, dst, data.ID)
} else {
noiseURL = fmt.Sprintf("https://unused/machine/webclient/init/%d/to/%d", data.Src, dst)
}
req, err := http.NewRequestWithContext(r.Context(), "POST", noiseURL, nil)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
resp, err := h.b.DoNoiseRequest(req)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer resp.Body.Close()
if _, err := io.Copy(w, resp.Body); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(resp.StatusCode)
}
func defBool(a string, def bool) bool { func defBool(a string, def bool) bool {
if a == "" { if a == "" {
return def return def