debugderp.go 9.1 KB

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