|
@@ -79,6 +79,7 @@ var handler = map[string]localAPIHandler{
|
|
|
"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
|