debugderp.go 8.8 KB

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