@ -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