2
0

debug.go 13 KB

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