debug.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build !ts_omit_debug
  4. package localapi
  5. import (
  6. "context"
  7. "encoding/json"
  8. "fmt"
  9. "io"
  10. "net"
  11. "net/http"
  12. "net/netip"
  13. "reflect"
  14. "slices"
  15. "strconv"
  16. "sync"
  17. "time"
  18. "tailscale.com/client/tailscale/apitype"
  19. "tailscale.com/feature"
  20. "tailscale.com/feature/buildfeatures"
  21. "tailscale.com/ipn"
  22. "tailscale.com/types/logger"
  23. "tailscale.com/util/eventbus"
  24. "tailscale.com/util/httpm"
  25. )
  26. func init() {
  27. Register("component-debug-logging", (*Handler).serveComponentDebugLogging)
  28. Register("debug", (*Handler).serveDebug)
  29. Register("dev-set-state-store", (*Handler).serveDevSetStateStore)
  30. Register("debug-bus-events", (*Handler).serveDebugBusEvents)
  31. Register("debug-bus-graph", (*Handler).serveEventBusGraph)
  32. Register("debug-derp-region", (*Handler).serveDebugDERPRegion)
  33. Register("debug-dial-types", (*Handler).serveDebugDialTypes)
  34. Register("debug-log", (*Handler).serveDebugLog)
  35. Register("debug-packet-filter-matches", (*Handler).serveDebugPacketFilterMatches)
  36. Register("debug-packet-filter-rules", (*Handler).serveDebugPacketFilterRules)
  37. Register("debug-peer-endpoint-changes", (*Handler).serveDebugPeerEndpointChanges)
  38. Register("debug-optional-features", (*Handler).serveDebugOptionalFeatures)
  39. }
  40. func (h *Handler) serveDebugPeerEndpointChanges(w http.ResponseWriter, r *http.Request) {
  41. if !h.PermitRead {
  42. http.Error(w, "status access denied", http.StatusForbidden)
  43. return
  44. }
  45. ipStr := r.FormValue("ip")
  46. if ipStr == "" {
  47. http.Error(w, "missing 'ip' parameter", http.StatusBadRequest)
  48. return
  49. }
  50. ip, err := netip.ParseAddr(ipStr)
  51. if err != nil {
  52. http.Error(w, "invalid IP", http.StatusBadRequest)
  53. return
  54. }
  55. w.Header().Set("Content-Type", "application/json")
  56. chs, err := h.b.GetPeerEndpointChanges(r.Context(), ip)
  57. if err != nil {
  58. http.Error(w, err.Error(), http.StatusInternalServerError)
  59. return
  60. }
  61. e := json.NewEncoder(w)
  62. e.SetIndent("", "\t")
  63. e.Encode(chs)
  64. }
  65. func (h *Handler) serveComponentDebugLogging(w http.ResponseWriter, r *http.Request) {
  66. if !h.PermitWrite {
  67. http.Error(w, "debug access denied", http.StatusForbidden)
  68. return
  69. }
  70. component := r.FormValue("component")
  71. secs, _ := strconv.Atoi(r.FormValue("secs"))
  72. err := h.b.SetComponentDebugLogging(component, h.clock.Now().Add(time.Duration(secs)*time.Second))
  73. var res struct {
  74. Error string
  75. }
  76. if err != nil {
  77. res.Error = err.Error()
  78. }
  79. w.Header().Set("Content-Type", "application/json")
  80. json.NewEncoder(w).Encode(res)
  81. }
  82. func (h *Handler) serveDebugDialTypes(w http.ResponseWriter, r *http.Request) {
  83. if !h.PermitWrite {
  84. http.Error(w, "debug-dial-types access denied", http.StatusForbidden)
  85. return
  86. }
  87. if r.Method != httpm.POST {
  88. http.Error(w, "only POST allowed", http.StatusMethodNotAllowed)
  89. return
  90. }
  91. ip := r.FormValue("ip")
  92. port := r.FormValue("port")
  93. network := r.FormValue("network")
  94. addr := ip + ":" + port
  95. if _, err := netip.ParseAddrPort(addr); err != nil {
  96. w.WriteHeader(http.StatusBadRequest)
  97. fmt.Fprintf(w, "invalid address %q: %v", addr, err)
  98. return
  99. }
  100. ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
  101. defer cancel()
  102. var bareDialer net.Dialer
  103. dialer := h.b.Dialer()
  104. var peerDialer net.Dialer
  105. peerDialer.Control = dialer.PeerDialControlFunc()
  106. // Kick off a dial with each available dialer in parallel.
  107. dialers := []struct {
  108. name string
  109. dial func(context.Context, string, string) (net.Conn, error)
  110. }{
  111. {"SystemDial", dialer.SystemDial},
  112. {"UserDial", dialer.UserDial},
  113. {"PeerDial", peerDialer.DialContext},
  114. {"BareDial", bareDialer.DialContext},
  115. }
  116. type result struct {
  117. name string
  118. conn net.Conn
  119. err error
  120. }
  121. results := make(chan result, len(dialers))
  122. var wg sync.WaitGroup
  123. for _, dialer := range dialers {
  124. dialer := dialer // loop capture
  125. wg.Add(1)
  126. go func() {
  127. defer wg.Done()
  128. conn, err := dialer.dial(ctx, network, addr)
  129. results <- result{dialer.name, conn, err}
  130. }()
  131. }
  132. wg.Wait()
  133. for range len(dialers) {
  134. res := <-results
  135. fmt.Fprintf(w, "[%s] connected=%v err=%v\n", res.name, res.conn != nil, res.err)
  136. if res.conn != nil {
  137. res.conn.Close()
  138. }
  139. }
  140. }
  141. func (h *Handler) serveDebug(w http.ResponseWriter, r *http.Request) {
  142. if !buildfeatures.HasDebug {
  143. http.Error(w, "debug not supported in this build", http.StatusNotImplemented)
  144. return
  145. }
  146. if !h.PermitWrite {
  147. http.Error(w, "debug access denied", http.StatusForbidden)
  148. return
  149. }
  150. if r.Method != httpm.POST {
  151. http.Error(w, "POST required", http.StatusMethodNotAllowed)
  152. return
  153. }
  154. // The action is normally in a POST form parameter, but
  155. // some actions (like "notify") want a full JSON body, so
  156. // permit some to have their action in a header.
  157. var action string
  158. switch v := r.Header.Get("Debug-Action"); v {
  159. case "notify":
  160. action = v
  161. default:
  162. action = r.FormValue("action")
  163. }
  164. var err error
  165. switch action {
  166. case "derp-set-homeless":
  167. h.b.MagicConn().SetHomeless(true)
  168. case "derp-unset-homeless":
  169. h.b.MagicConn().SetHomeless(false)
  170. case "rebind":
  171. err = h.b.DebugRebind()
  172. case "restun":
  173. err = h.b.DebugReSTUN()
  174. case "notify":
  175. var n ipn.Notify
  176. err = json.NewDecoder(r.Body).Decode(&n)
  177. if err != nil {
  178. break
  179. }
  180. h.b.DebugNotify(n)
  181. case "notify-last-netmap":
  182. h.b.DebugNotifyLastNetMap()
  183. case "break-tcp-conns":
  184. err = h.b.DebugBreakTCPConns()
  185. case "break-derp-conns":
  186. err = h.b.DebugBreakDERPConns()
  187. case "force-netmap-update":
  188. h.b.DebugForceNetmapUpdate()
  189. case "control-knobs":
  190. k := h.b.ControlKnobs()
  191. w.Header().Set("Content-Type", "application/json")
  192. err = json.NewEncoder(w).Encode(k.AsDebugJSON())
  193. if err == nil {
  194. return
  195. }
  196. case "pick-new-derp":
  197. err = h.b.DebugPickNewDERP()
  198. case "force-prefer-derp":
  199. var n int
  200. err = json.NewDecoder(r.Body).Decode(&n)
  201. if err != nil {
  202. break
  203. }
  204. h.b.DebugForcePreferDERP(n)
  205. case "peer-relay-servers":
  206. servers := h.b.DebugPeerRelayServers().Slice()
  207. slices.SortFunc(servers, func(a, b netip.Addr) int {
  208. return a.Compare(b)
  209. })
  210. err = json.NewEncoder(w).Encode(servers)
  211. if err == nil {
  212. return
  213. }
  214. case "":
  215. err = fmt.Errorf("missing parameter 'action'")
  216. default:
  217. err = fmt.Errorf("unknown action %q", action)
  218. }
  219. if err != nil {
  220. http.Error(w, err.Error(), http.StatusBadRequest)
  221. return
  222. }
  223. w.Header().Set("Content-Type", "text/plain")
  224. io.WriteString(w, "done\n")
  225. }
  226. func (h *Handler) serveDevSetStateStore(w http.ResponseWriter, r *http.Request) {
  227. if !h.PermitWrite {
  228. http.Error(w, "debug access denied", http.StatusForbidden)
  229. return
  230. }
  231. if r.Method != httpm.POST {
  232. http.Error(w, "POST required", http.StatusMethodNotAllowed)
  233. return
  234. }
  235. if err := h.b.SetDevStateStore(r.FormValue("key"), r.FormValue("value")); err != nil {
  236. http.Error(w, err.Error(), http.StatusInternalServerError)
  237. return
  238. }
  239. w.Header().Set("Content-Type", "text/plain")
  240. io.WriteString(w, "done\n")
  241. }
  242. func (h *Handler) serveDebugPacketFilterRules(w http.ResponseWriter, r *http.Request) {
  243. if !h.PermitWrite {
  244. http.Error(w, "debug access denied", http.StatusForbidden)
  245. return
  246. }
  247. nm := h.b.NetMap()
  248. if nm == nil {
  249. http.Error(w, "no netmap", http.StatusNotFound)
  250. return
  251. }
  252. w.Header().Set("Content-Type", "application/json")
  253. enc := json.NewEncoder(w)
  254. enc.SetIndent("", "\t")
  255. enc.Encode(nm.PacketFilterRules)
  256. }
  257. func (h *Handler) serveDebugPacketFilterMatches(w http.ResponseWriter, r *http.Request) {
  258. if !h.PermitWrite {
  259. http.Error(w, "debug access denied", http.StatusForbidden)
  260. return
  261. }
  262. nm := h.b.NetMap()
  263. if nm == nil {
  264. http.Error(w, "no netmap", http.StatusNotFound)
  265. return
  266. }
  267. w.Header().Set("Content-Type", "application/json")
  268. enc := json.NewEncoder(w)
  269. enc.SetIndent("", "\t")
  270. enc.Encode(nm.PacketFilter)
  271. }
  272. // debugEventError provides the JSON encoding of internal errors from event processing.
  273. type debugEventError struct {
  274. Error string
  275. }
  276. // serveDebugBusEvents taps into the tailscaled/utils/eventbus and streams
  277. // events to the client.
  278. func (h *Handler) serveDebugBusEvents(w http.ResponseWriter, r *http.Request) {
  279. // Require write access (~root) as the logs could contain something
  280. // sensitive.
  281. if !h.PermitWrite {
  282. http.Error(w, "event bus access denied", http.StatusForbidden)
  283. return
  284. }
  285. if r.Method != httpm.GET {
  286. http.Error(w, "GET required", http.StatusMethodNotAllowed)
  287. return
  288. }
  289. bus, ok := h.LocalBackend().Sys().Bus.GetOK()
  290. if !ok {
  291. http.Error(w, "event bus not running", http.StatusNoContent)
  292. return
  293. }
  294. f, ok := w.(http.Flusher)
  295. if !ok {
  296. http.Error(w, "streaming unsupported", http.StatusInternalServerError)
  297. return
  298. }
  299. io.WriteString(w, `{"Event":"[event listener connected]\n"}`+"\n")
  300. f.Flush()
  301. mon := bus.Debugger().WatchBus()
  302. defer mon.Close()
  303. i := 0
  304. for {
  305. select {
  306. case <-r.Context().Done():
  307. fmt.Fprintf(w, `{"Event":"[event listener closed]\n"}`)
  308. return
  309. case <-mon.Done():
  310. return
  311. case event := <-mon.Events():
  312. data := eventbus.DebugEvent{
  313. Count: i,
  314. Type: reflect.TypeOf(event.Event).String(),
  315. Event: event.Event,
  316. From: event.From.Name(),
  317. }
  318. for _, client := range event.To {
  319. data.To = append(data.To, client.Name())
  320. }
  321. if msg, err := json.Marshal(data); err != nil {
  322. data.Event = debugEventError{Error: fmt.Sprintf(
  323. "failed to marshal JSON for %T", event.Event,
  324. )}
  325. if errMsg, err := json.Marshal(data); err != nil {
  326. fmt.Fprintf(w,
  327. `{"Count": %d, "Event":"[ERROR] failed to marshal JSON for %T\n"}`,
  328. i, event.Event)
  329. } else {
  330. w.Write(errMsg)
  331. }
  332. } else {
  333. w.Write(msg)
  334. }
  335. f.Flush()
  336. i++
  337. }
  338. }
  339. }
  340. // serveEventBusGraph taps into the event bus and dumps out the active graph of
  341. // publishers and subscribers. It does not represent anything about the messages
  342. // exchanged.
  343. func (h *Handler) serveEventBusGraph(w http.ResponseWriter, r *http.Request) {
  344. if r.Method != httpm.GET {
  345. http.Error(w, "GET required", http.StatusMethodNotAllowed)
  346. return
  347. }
  348. bus, ok := h.LocalBackend().Sys().Bus.GetOK()
  349. if !ok {
  350. http.Error(w, "event bus not running", http.StatusPreconditionFailed)
  351. return
  352. }
  353. debugger := bus.Debugger()
  354. clients := debugger.Clients()
  355. graph := map[string]eventbus.DebugTopic{}
  356. for _, client := range clients {
  357. for _, pub := range debugger.PublishTypes(client) {
  358. topic, ok := graph[pub.Name()]
  359. if !ok {
  360. topic = eventbus.DebugTopic{Name: pub.Name()}
  361. }
  362. topic.Publisher = client.Name()
  363. graph[pub.Name()] = topic
  364. }
  365. for _, sub := range debugger.SubscribeTypes(client) {
  366. topic, ok := graph[sub.Name()]
  367. if !ok {
  368. topic = eventbus.DebugTopic{Name: sub.Name()}
  369. }
  370. topic.Subscribers = append(topic.Subscribers, client.Name())
  371. graph[sub.Name()] = topic
  372. }
  373. }
  374. // The top level map is not really needed for the client, convert to a list.
  375. topics := eventbus.DebugTopics{}
  376. for _, v := range graph {
  377. topics.Topics = append(topics.Topics, v)
  378. }
  379. w.Header().Set("Content-Type", "application/json")
  380. json.NewEncoder(w).Encode(topics)
  381. }
  382. func (h *Handler) serveDebugLog(w http.ResponseWriter, r *http.Request) {
  383. if !buildfeatures.HasLogTail {
  384. http.Error(w, feature.ErrUnavailable.Error(), http.StatusNotImplemented)
  385. return
  386. }
  387. if !h.PermitRead {
  388. http.Error(w, "debug-log access denied", http.StatusForbidden)
  389. return
  390. }
  391. if r.Method != httpm.POST {
  392. http.Error(w, "only POST allowed", http.StatusMethodNotAllowed)
  393. return
  394. }
  395. defer h.b.TryFlushLogs() // kick off upload after we're done logging
  396. type logRequestJSON struct {
  397. Lines []string
  398. Prefix string
  399. }
  400. var logRequest logRequestJSON
  401. if err := json.NewDecoder(r.Body).Decode(&logRequest); err != nil {
  402. http.Error(w, "invalid JSON body", http.StatusBadRequest)
  403. return
  404. }
  405. prefix := logRequest.Prefix
  406. if prefix == "" {
  407. prefix = "debug-log"
  408. }
  409. logf := logger.WithPrefix(h.logf, prefix+": ")
  410. // We can write logs too fast for logtail to handle, even when
  411. // opting-out of rate limits. Limit ourselves to at most one message
  412. // per 20ms and a burst of 60 log lines, which should be fast enough to
  413. // not block for too long but slow enough that we can upload all lines.
  414. logf = logger.SlowLoggerWithClock(r.Context(), logf, 20*time.Millisecond, 60, h.clock.Now)
  415. for _, line := range logRequest.Lines {
  416. logf("%s", line)
  417. }
  418. w.WriteHeader(http.StatusNoContent)
  419. }
  420. func (h *Handler) serveDebugOptionalFeatures(w http.ResponseWriter, r *http.Request) {
  421. of := &apitype.OptionalFeatures{
  422. Features: feature.Registered(),
  423. }
  424. w.Header().Set("Content-Type", "application/json")
  425. json.NewEncoder(w).Encode(of)
  426. }