tsweb.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Package tsweb contains code used in various Tailscale webservers.
  4. package tsweb
  5. import (
  6. "bufio"
  7. "bytes"
  8. "context"
  9. "errors"
  10. "expvar"
  11. "fmt"
  12. "net"
  13. "net/http"
  14. _ "net/http/pprof"
  15. "net/netip"
  16. "os"
  17. "path/filepath"
  18. "strconv"
  19. "strings"
  20. "sync"
  21. "time"
  22. "go4.org/mem"
  23. "tailscale.com/envknob"
  24. "tailscale.com/net/tsaddr"
  25. "tailscale.com/tsweb/varz"
  26. "tailscale.com/types/logger"
  27. "tailscale.com/util/cmpx"
  28. "tailscale.com/util/vizerror"
  29. )
  30. // DevMode controls whether extra output in shown, for when the binary is being run in dev mode.
  31. var DevMode bool
  32. func DefaultCertDir(leafDir string) string {
  33. cacheDir, err := os.UserCacheDir()
  34. if err == nil {
  35. return filepath.Join(cacheDir, "tailscale", leafDir)
  36. }
  37. return ""
  38. }
  39. // IsProd443 reports whether addr is a Go listen address for port 443.
  40. func IsProd443(addr string) bool {
  41. _, port, _ := net.SplitHostPort(addr)
  42. return port == "443" || port == "https"
  43. }
  44. // AllowDebugAccess reports whether r should be permitted to access
  45. // various debug endpoints.
  46. func AllowDebugAccess(r *http.Request) bool {
  47. if r.Header.Get("X-Forwarded-For") != "" {
  48. // TODO if/when needed. For now, conservative:
  49. return false
  50. }
  51. ipStr, _, err := net.SplitHostPort(r.RemoteAddr)
  52. if err != nil {
  53. return false
  54. }
  55. ip, err := netip.ParseAddr(ipStr)
  56. if err != nil {
  57. return false
  58. }
  59. if tsaddr.IsTailscaleIP(ip) || ip.IsLoopback() || ipStr == envknob.String("TS_ALLOW_DEBUG_IP") {
  60. return true
  61. }
  62. if r.Method == "GET" {
  63. urlKey := r.FormValue("debugkey")
  64. keyPath := envknob.String("TS_DEBUG_KEY_PATH")
  65. if urlKey != "" && keyPath != "" {
  66. slurp, err := os.ReadFile(keyPath)
  67. if err == nil && string(bytes.TrimSpace(slurp)) == urlKey {
  68. return true
  69. }
  70. }
  71. }
  72. return false
  73. }
  74. // AcceptsEncoding reports whether r accepts the named encoding
  75. // ("gzip", "br", etc).
  76. func AcceptsEncoding(r *http.Request, enc string) bool {
  77. h := r.Header.Get("Accept-Encoding")
  78. if h == "" {
  79. return false
  80. }
  81. if !strings.Contains(h, enc) && !mem.ContainsFold(mem.S(h), mem.S(enc)) {
  82. return false
  83. }
  84. remain := h
  85. for len(remain) > 0 {
  86. var part string
  87. part, remain, _ = strings.Cut(remain, ",")
  88. part = strings.TrimSpace(part)
  89. part, _, _ = strings.Cut(part, ";")
  90. if part == enc {
  91. return true
  92. }
  93. }
  94. return false
  95. }
  96. // Protected wraps a provided debug handler, h, returning a Handler
  97. // that enforces AllowDebugAccess and returns forbidden replies for
  98. // unauthorized requests.
  99. func Protected(h http.Handler) http.Handler {
  100. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  101. if !AllowDebugAccess(r) {
  102. msg := "debug access denied"
  103. if DevMode {
  104. ipStr, _, _ := net.SplitHostPort(r.RemoteAddr)
  105. msg += fmt.Sprintf("; to permit access, set TS_ALLOW_DEBUG_IP=%v", ipStr)
  106. }
  107. http.Error(w, msg, http.StatusForbidden)
  108. return
  109. }
  110. h.ServeHTTP(w, r)
  111. })
  112. }
  113. // Port80Handler is the handler to be given to
  114. // autocert.Manager.HTTPHandler. The inner handler is the mux
  115. // returned by NewMux containing registered /debug handlers.
  116. type Port80Handler struct {
  117. Main http.Handler
  118. // FQDN is used to redirect incoming requests to https://<FQDN>.
  119. // If it is not set, the hostname is calculated from the incoming
  120. // request.
  121. FQDN string
  122. }
  123. func (h Port80Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  124. path := r.RequestURI
  125. if path == "/debug" || strings.HasPrefix(path, "/debug") {
  126. h.Main.ServeHTTP(w, r)
  127. return
  128. }
  129. if r.Method != "GET" && r.Method != "HEAD" {
  130. http.Error(w, "Use HTTPS", http.StatusBadRequest)
  131. return
  132. }
  133. if path == "/" && AllowDebugAccess(r) {
  134. // Redirect authorized user to the debug handler.
  135. path = "/debug/"
  136. }
  137. host := cmpx.Or(h.FQDN, r.Host)
  138. target := "https://" + host + path
  139. http.Redirect(w, r, target, http.StatusFound)
  140. }
  141. // ReturnHandler is like net/http.Handler, but the handler can return an
  142. // error instead of writing to its ResponseWriter.
  143. type ReturnHandler interface {
  144. // ServeHTTPReturn is like http.Handler.ServeHTTP, except that
  145. // it can choose to return an error instead of writing to its
  146. // http.ResponseWriter.
  147. //
  148. // If ServeHTTPReturn returns an error, it caller should handle
  149. // an error by serving an HTTP 500 response to the user. The
  150. // error details should not be sent to the client, as they may
  151. // contain sensitive information. If the error is an
  152. // HTTPError, though, callers should use the HTTP response
  153. // code and message as the response to the client.
  154. ServeHTTPReturn(http.ResponseWriter, *http.Request) error
  155. }
  156. type HandlerOptions struct {
  157. QuietLoggingIfSuccessful bool // if set, do not log successfully handled HTTP requests (200 and 304 status codes)
  158. Logf logger.Logf
  159. Now func() time.Time // if nil, defaults to time.Now
  160. // If non-nil, StatusCodeCounters maintains counters
  161. // of status codes for handled responses.
  162. // The keys are "1xx", "2xx", "3xx", "4xx", and "5xx".
  163. StatusCodeCounters *expvar.Map
  164. // If non-nil, StatusCodeCountersFull maintains counters of status
  165. // codes for handled responses.
  166. // The keys are HTTP numeric response codes e.g. 200, 404, ...
  167. StatusCodeCountersFull *expvar.Map
  168. // OnError is called if the handler returned a HTTPError. This
  169. // is intended to be used to present pretty error pages if
  170. // the user agent is determined to be a browser.
  171. OnError ErrorHandlerFunc
  172. }
  173. // ErrorHandlerFunc is called to present a error response.
  174. type ErrorHandlerFunc func(http.ResponseWriter, *http.Request, HTTPError)
  175. // ReturnHandlerFunc is an adapter to allow the use of ordinary
  176. // functions as ReturnHandlers. If f is a function with the
  177. // appropriate signature, ReturnHandlerFunc(f) is a ReturnHandler that
  178. // calls f.
  179. type ReturnHandlerFunc func(http.ResponseWriter, *http.Request) error
  180. // ServeHTTPReturn calls f(w, r).
  181. func (f ReturnHandlerFunc) ServeHTTPReturn(w http.ResponseWriter, r *http.Request) error {
  182. return f(w, r)
  183. }
  184. // StdHandler converts a ReturnHandler into a standard http.Handler.
  185. // Handled requests are logged using opts.Logf, as are any errors.
  186. // Errors are handled as specified by the Handler interface.
  187. func StdHandler(h ReturnHandler, opts HandlerOptions) http.Handler {
  188. if opts.Now == nil {
  189. opts.Now = time.Now
  190. }
  191. if opts.Logf == nil {
  192. opts.Logf = logger.Discard
  193. }
  194. return retHandler{h, opts}
  195. }
  196. // retHandler is an http.Handler that wraps a Handler and handles errors.
  197. type retHandler struct {
  198. rh ReturnHandler
  199. opts HandlerOptions
  200. }
  201. // ServeHTTP implements the http.Handler interface.
  202. func (h retHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  203. msg := AccessLogRecord{
  204. When: h.opts.Now(),
  205. RemoteAddr: r.RemoteAddr,
  206. Proto: r.Proto,
  207. TLS: r.TLS != nil,
  208. Host: r.Host,
  209. Method: r.Method,
  210. RequestURI: r.URL.RequestURI(),
  211. UserAgent: r.UserAgent(),
  212. Referer: r.Referer(),
  213. }
  214. lw := &loggingResponseWriter{ResponseWriter: w, logf: h.opts.Logf}
  215. err := h.rh.ServeHTTPReturn(lw, r)
  216. var hErr HTTPError
  217. var hErrOK bool
  218. if errors.As(err, &hErr) {
  219. hErrOK = true
  220. } else if vizErr, ok := vizerror.As(err); ok {
  221. hErrOK = true
  222. hErr = HTTPError{Msg: vizErr.Error()}
  223. }
  224. if lw.code == 0 && err == nil && !lw.hijacked {
  225. // If the handler didn't write and didn't send a header, that still means 200.
  226. // (See https://play.golang.org/p/4P7nx_Tap7p)
  227. lw.code = 200
  228. }
  229. msg.Seconds = h.opts.Now().Sub(msg.When).Seconds()
  230. msg.Code = lw.code
  231. msg.Bytes = lw.bytes
  232. switch {
  233. case lw.hijacked:
  234. // Connection no longer belongs to us, just log that we
  235. // switched protocols away from HTTP.
  236. if msg.Code == 0 {
  237. msg.Code = http.StatusSwitchingProtocols
  238. }
  239. case err != nil && r.Context().Err() == context.Canceled:
  240. msg.Code = 499 // nginx convention: Client Closed Request
  241. msg.Err = context.Canceled.Error()
  242. case hErrOK:
  243. // Handler asked us to send an error. Do so, if we haven't
  244. // already sent a response.
  245. msg.Err = hErr.Msg
  246. if hErr.Err != nil {
  247. if msg.Err == "" {
  248. msg.Err = hErr.Err.Error()
  249. } else {
  250. msg.Err = msg.Err + ": " + hErr.Err.Error()
  251. }
  252. }
  253. if lw.code != 0 {
  254. h.opts.Logf("[unexpected] handler returned HTTPError %v, but already sent a response with code %d", hErr, lw.code)
  255. break
  256. }
  257. msg.Code = hErr.Code
  258. if msg.Code == 0 {
  259. h.opts.Logf("[unexpected] HTTPError %v did not contain an HTTP status code, sending internal server error", hErr)
  260. msg.Code = http.StatusInternalServerError
  261. }
  262. if h.opts.OnError != nil {
  263. h.opts.OnError(lw, r, hErr)
  264. } else {
  265. // Default headers set by http.Error.
  266. lw.Header().Set("Content-Type", "text/plain; charset=utf-8")
  267. lw.Header().Set("X-Content-Type-Options", "nosniff")
  268. for k, vs := range hErr.Header {
  269. lw.Header()[k] = vs
  270. }
  271. lw.WriteHeader(msg.Code)
  272. fmt.Fprintln(lw, hErr.Msg)
  273. }
  274. case err != nil:
  275. // Handler returned a generic error. Serve an internal server
  276. // error, if necessary.
  277. msg.Err = err.Error()
  278. if lw.code == 0 {
  279. msg.Code = http.StatusInternalServerError
  280. http.Error(lw, "internal server error", msg.Code)
  281. }
  282. }
  283. if !h.opts.QuietLoggingIfSuccessful || (msg.Code != http.StatusOK && msg.Code != http.StatusNotModified) {
  284. h.opts.Logf("%s", msg)
  285. }
  286. if h.opts.StatusCodeCounters != nil {
  287. h.opts.StatusCodeCounters.Add(responseCodeString(msg.Code/100), 1)
  288. }
  289. if h.opts.StatusCodeCountersFull != nil {
  290. h.opts.StatusCodeCountersFull.Add(responseCodeString(msg.Code), 1)
  291. }
  292. }
  293. func responseCodeString(code int) string {
  294. if v, ok := responseCodeCache.Load(code); ok {
  295. return v.(string)
  296. }
  297. var ret string
  298. if code < 10 {
  299. ret = fmt.Sprintf("%dxx", code)
  300. } else {
  301. ret = strconv.Itoa(code)
  302. }
  303. responseCodeCache.Store(code, ret)
  304. return ret
  305. }
  306. // responseCodeCache memoizes the string form of HTTP response codes,
  307. // so that the hot request-handling codepath doesn't have to allocate
  308. // in strconv/fmt for every request.
  309. //
  310. // Keys are either full HTTP response code ints (200, 404) or "family"
  311. // ints representing entire families (e.g. 2 for 2xx codes). Values
  312. // are the string form of that code/family.
  313. var responseCodeCache sync.Map
  314. // loggingResponseWriter wraps a ResponseWriter and record the HTTP
  315. // response code that gets sent, if any.
  316. type loggingResponseWriter struct {
  317. http.ResponseWriter
  318. code int
  319. bytes int
  320. hijacked bool
  321. logf logger.Logf
  322. }
  323. // WriteHeader implements http.Handler.
  324. func (l *loggingResponseWriter) WriteHeader(statusCode int) {
  325. if l.code != 0 {
  326. l.logf("[unexpected] HTTP handler set statusCode twice (%d and %d)", l.code, statusCode)
  327. return
  328. }
  329. l.code = statusCode
  330. l.ResponseWriter.WriteHeader(statusCode)
  331. }
  332. // Write implements http.Handler.
  333. func (l *loggingResponseWriter) Write(bs []byte) (int, error) {
  334. if l.code == 0 {
  335. l.code = 200
  336. }
  337. n, err := l.ResponseWriter.Write(bs)
  338. l.bytes += n
  339. return n, err
  340. }
  341. // Hijack implements http.Hijacker. Note that hijacking can still fail
  342. // because the wrapped ResponseWriter is not required to implement
  343. // Hijacker, as this breaks HTTP/2.
  344. func (l *loggingResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  345. h, ok := l.ResponseWriter.(http.Hijacker)
  346. if !ok {
  347. return nil, nil, errors.New("ResponseWriter is not a Hijacker")
  348. }
  349. conn, buf, err := h.Hijack()
  350. if err == nil {
  351. l.hijacked = true
  352. }
  353. return conn, buf, err
  354. }
  355. func (l loggingResponseWriter) Flush() {
  356. f, _ := l.ResponseWriter.(http.Flusher)
  357. if f == nil {
  358. l.logf("[unexpected] tried to Flush a ResponseWriter that can't flush")
  359. return
  360. }
  361. f.Flush()
  362. }
  363. // HTTPError is an error with embedded HTTP response information.
  364. //
  365. // It is the error type to be (optionally) used by Handler.ServeHTTPReturn.
  366. type HTTPError struct {
  367. Code int // HTTP response code to send to client; 0 means 500
  368. Msg string // Response body to send to client
  369. Err error // Detailed error to log on the server
  370. Header http.Header // Optional set of HTTP headers to set in the response
  371. }
  372. // Error implements the error interface.
  373. func (e HTTPError) Error() string { return fmt.Sprintf("httperror{%d, %q, %v}", e.Code, e.Msg, e.Err) }
  374. func (e HTTPError) Unwrap() error { return e.Err }
  375. // Error returns an HTTPError containing the given information.
  376. func Error(code int, msg string, err error) HTTPError {
  377. return HTTPError{Code: code, Msg: msg, Err: err}
  378. }
  379. // VarzHandler writes expvar values as Prometheus metrics.
  380. // TODO: migrate all users to varz.Handler or promvarz.Handler and remove this.
  381. func VarzHandler(w http.ResponseWriter, r *http.Request) {
  382. varz.Handler(w, r)
  383. }