debugderp.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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. "cmp"
  7. "context"
  8. "crypto/tls"
  9. "encoding/json"
  10. "fmt"
  11. "net"
  12. "net/http"
  13. "net/netip"
  14. "strconv"
  15. "time"
  16. "tailscale.com/derp/derphttp"
  17. "tailscale.com/ipn/ipnstate"
  18. "tailscale.com/net/netaddr"
  19. "tailscale.com/net/netns"
  20. "tailscale.com/net/stun"
  21. "tailscale.com/tailcfg"
  22. "tailscale.com/types/key"
  23. "tailscale.com/types/nettype"
  24. )
  25. func (h *Handler) serveDebugDERPRegion(w http.ResponseWriter, r *http.Request) {
  26. if !h.PermitWrite {
  27. http.Error(w, "debug access denied", http.StatusForbidden)
  28. return
  29. }
  30. if r.Method != "POST" {
  31. http.Error(w, "POST required", http.StatusMethodNotAllowed)
  32. return
  33. }
  34. var st ipnstate.DebugDERPRegionReport
  35. defer func() {
  36. j, _ := json.Marshal(st)
  37. w.Header().Set("Content-Type", "application/json")
  38. w.Write(j)
  39. }()
  40. dm := h.b.DERPMap()
  41. if dm == nil {
  42. st.Errors = append(st.Errors, "no DERP map (not connected?)")
  43. return
  44. }
  45. regStr := r.FormValue("region")
  46. var reg *tailcfg.DERPRegion
  47. if id, err := strconv.Atoi(regStr); err == nil {
  48. reg = dm.Regions[id]
  49. } else {
  50. for _, r := range dm.Regions {
  51. if r.RegionCode == regStr {
  52. reg = r
  53. break
  54. }
  55. }
  56. }
  57. if reg == nil {
  58. st.Errors = append(st.Errors, fmt.Sprintf("no such region %q in DERP map", regStr))
  59. return
  60. }
  61. st.Info = append(st.Info, fmt.Sprintf("Region %v == %q", reg.RegionID, reg.RegionCode))
  62. if len(dm.Regions) == 1 {
  63. st.Warnings = append(st.Warnings, "Having only a single DERP region (i.e. removing the default Tailscale-provided regions) is a single point of failure and could hamper connectivity")
  64. }
  65. if reg.Avoid {
  66. st.Warnings = append(st.Warnings, "Region is marked with Avoid bit")
  67. }
  68. if len(reg.Nodes) == 0 {
  69. st.Errors = append(st.Errors, "Region has no nodes defined")
  70. return
  71. }
  72. ctx := r.Context()
  73. var (
  74. dialer net.Dialer
  75. client *http.Client = http.DefaultClient
  76. )
  77. checkConn := func(derpNode *tailcfg.DERPNode) bool {
  78. port := cmp.Or(derpNode.DERPPort, 443)
  79. var (
  80. hasIPv4 bool
  81. hasIPv6 bool
  82. )
  83. // Check IPv4 first
  84. addr := net.JoinHostPort(cmp.Or(derpNode.IPv4, derpNode.HostName), strconv.Itoa(port))
  85. conn, err := dialer.DialContext(ctx, "tcp4", addr)
  86. if err != nil {
  87. st.Errors = append(st.Errors, fmt.Sprintf("Error connecting to node %q @ %q over IPv4: %v", derpNode.HostName, addr, err))
  88. } else {
  89. defer conn.Close()
  90. // Upgrade to TLS and verify that works properly.
  91. tlsConn := tls.Client(conn, &tls.Config{
  92. ServerName: cmp.Or(derpNode.CertName, derpNode.HostName),
  93. })
  94. if err := tlsConn.HandshakeContext(ctx); err != nil {
  95. st.Errors = append(st.Errors, fmt.Sprintf("Error upgrading connection to node %q @ %q to TLS over IPv4: %v", derpNode.HostName, addr, err))
  96. } else {
  97. hasIPv4 = true
  98. }
  99. }
  100. // Check IPv6
  101. addr = net.JoinHostPort(cmp.Or(derpNode.IPv6, derpNode.HostName), strconv.Itoa(port))
  102. conn, err = dialer.DialContext(ctx, "tcp6", addr)
  103. if err != nil {
  104. st.Errors = append(st.Errors, fmt.Sprintf("Error connecting to node %q @ %q over IPv6: %v", derpNode.HostName, addr, err))
  105. } else {
  106. defer conn.Close()
  107. // Upgrade to TLS and verify that works properly.
  108. tlsConn := tls.Client(conn, &tls.Config{
  109. ServerName: cmp.Or(derpNode.CertName, derpNode.HostName),
  110. // TODO(andrew-d): we should print more
  111. // detailed failure information on if/why TLS
  112. // verification fails
  113. })
  114. if err := tlsConn.HandshakeContext(ctx); err != nil {
  115. st.Errors = append(st.Errors, fmt.Sprintf("Error upgrading connection to node %q @ %q to TLS over IPv6: %v", derpNode.HostName, addr, err))
  116. } else {
  117. hasIPv6 = true
  118. }
  119. }
  120. // If we only have an IPv6 conn, then warn; we want both.
  121. if hasIPv6 && !hasIPv4 {
  122. st.Warnings = append(st.Warnings, fmt.Sprintf("Node %q only has IPv6 connectivity, not IPv4", derpNode.HostName))
  123. } else if hasIPv6 && hasIPv4 {
  124. st.Info = append(st.Info, fmt.Sprintf("Node %q has working IPv4 and IPv6 connectivity", derpNode.HostName))
  125. }
  126. return hasIPv4 || hasIPv6
  127. }
  128. checkSTUN4 := func(derpNode *tailcfg.DERPNode) {
  129. u4, err := nettype.MakePacketListenerWithNetIP(netns.Listener(h.logf, h.b.NetMon())).ListenPacket(ctx, "udp4", ":0")
  130. if err != nil {
  131. st.Errors = append(st.Errors, fmt.Sprintf("Error creating IPv4 STUN listener: %v", err))
  132. return
  133. }
  134. defer u4.Close()
  135. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  136. defer cancel()
  137. var addr netip.Addr
  138. if derpNode.IPv4 != "" {
  139. addr, err = netip.ParseAddr(derpNode.IPv4)
  140. if err != nil {
  141. // Error printed elsewhere
  142. return
  143. }
  144. } else {
  145. addrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip4", derpNode.HostName)
  146. if err != nil {
  147. st.Errors = append(st.Errors, fmt.Sprintf("Error resolving node %q IPv4 addresses: %v", derpNode.HostName, err))
  148. return
  149. }
  150. addr = addrs[0]
  151. }
  152. addrPort := netip.AddrPortFrom(addr, uint16(cmp.Or(derpNode.STUNPort, 3478)))
  153. txID := stun.NewTxID()
  154. req := stun.Request(txID)
  155. done := make(chan struct{})
  156. defer close(done)
  157. go func() {
  158. select {
  159. case <-ctx.Done():
  160. case <-done:
  161. }
  162. u4.Close()
  163. }()
  164. gotResponse := make(chan netip.AddrPort, 1)
  165. go func() {
  166. defer u4.Close()
  167. var buf [64 << 10]byte
  168. for {
  169. n, addr, err := u4.ReadFromUDPAddrPort(buf[:])
  170. if err != nil {
  171. return
  172. }
  173. pkt := buf[:n]
  174. if !stun.Is(pkt) {
  175. continue
  176. }
  177. ap := netaddr.Unmap(addr)
  178. if !ap.IsValid() {
  179. continue
  180. }
  181. tx, addrPort, err := stun.ParseResponse(pkt)
  182. if err != nil {
  183. continue
  184. }
  185. if tx == txID {
  186. gotResponse <- addrPort
  187. return
  188. }
  189. }
  190. }()
  191. _, err = u4.WriteToUDPAddrPort(req, addrPort)
  192. if err != nil {
  193. st.Errors = append(st.Errors, fmt.Sprintf("Error sending IPv4 STUN packet to %v (%q): %v", addrPort, derpNode.HostName, err))
  194. return
  195. }
  196. select {
  197. case resp := <-gotResponse:
  198. st.Info = append(st.Info, fmt.Sprintf("Node %q returned IPv4 STUN response: %v", derpNode.HostName, resp))
  199. case <-ctx.Done():
  200. st.Warnings = append(st.Warnings, fmt.Sprintf("Node %q did not return a IPv4 STUN response", derpNode.HostName))
  201. }
  202. }
  203. // Start by checking whether we can establish a HTTP connection
  204. for _, derpNode := range reg.Nodes {
  205. if !derpNode.STUNOnly {
  206. connSuccess := checkConn(derpNode)
  207. // Verify that the /generate_204 endpoint works
  208. captivePortalURL := fmt.Sprintf("http://%s/generate_204?t=%d", derpNode.HostName, time.Now().Unix())
  209. req, err := http.NewRequest("GET", captivePortalURL, nil)
  210. if err != nil {
  211. st.Warnings = append(st.Warnings, fmt.Sprintf("Internal error creating request for captive portal check: %v", err))
  212. continue
  213. }
  214. req.Header.Set("Cache-Control", "no-cache, no-store, must-revalidate, no-transform, max-age=0")
  215. resp, err := client.Do(req)
  216. if err != nil {
  217. st.Warnings = append(st.Warnings, fmt.Sprintf("Error making request to the captive portal check %q; is port 80 blocked?", captivePortalURL))
  218. } else {
  219. resp.Body.Close()
  220. }
  221. if !connSuccess {
  222. continue
  223. }
  224. fakePrivKey := key.NewNode()
  225. // Next, repeatedly get the server key to see if the node is
  226. // behind a load balancer (incorrectly).
  227. serverPubKeys := make(map[key.NodePublic]bool)
  228. for i := range 5 {
  229. func() {
  230. rc := derphttp.NewRegionClient(fakePrivKey, h.logf, h.b.NetMon(), func() *tailcfg.DERPRegion {
  231. return &tailcfg.DERPRegion{
  232. RegionID: reg.RegionID,
  233. RegionCode: reg.RegionCode,
  234. RegionName: reg.RegionName,
  235. Nodes: []*tailcfg.DERPNode{derpNode},
  236. }
  237. })
  238. if err := rc.Connect(ctx); err != nil {
  239. st.Errors = append(st.Errors, fmt.Sprintf("Error connecting to node %q @ try %d: %v", derpNode.HostName, i, err))
  240. return
  241. }
  242. if len(serverPubKeys) == 0 {
  243. st.Info = append(st.Info, fmt.Sprintf("Successfully established a DERP connection with node %q", derpNode.HostName))
  244. }
  245. serverPubKeys[rc.ServerPublicKey()] = true
  246. }()
  247. }
  248. if len(serverPubKeys) > 1 {
  249. st.Errors = append(st.Errors, fmt.Sprintf("Received multiple server public keys (%d); is the DERP server behind a load balancer?", len(serverPubKeys)))
  250. }
  251. } else {
  252. st.Info = append(st.Info, fmt.Sprintf("Node %q is marked STUNOnly; skipped non-STUN checks", derpNode.HostName))
  253. }
  254. // Send a STUN query to this node to verify whether or not it
  255. // correctly returns an IP address.
  256. checkSTUN4(derpNode)
  257. }
  258. // TODO(bradfitz): finish:
  259. // * try to DERP auth with new public key.
  260. // * if rejected, add Info that it's likely the DERP server authz is on,
  261. // try with LocalBackend's node key instead.
  262. // * if they have more then one node, try to relay a packet between them
  263. // and see if it works (like cmd/derpprobe). But if server authz is on,
  264. // we won't be able to, so just warn. Say to turn that off, try again,
  265. // then turn it back on. TODO(bradfitz): maybe add a debug frame to DERP
  266. // protocol to say how many peers it's meshed with. Should match count
  267. // in DERPRegion. Or maybe even list all their server pub keys that it's peered
  268. // with.
  269. // * If their certificate is bad, either expired or just wrongly
  270. // issued in the first place, tell them specifically that the
  271. // cert is bad not just that the connection failed.
  272. }