server.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package ipnserver
  4. import (
  5. "context"
  6. "encoding/json"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "net"
  11. "net/http"
  12. "os/user"
  13. "strconv"
  14. "strings"
  15. "sync"
  16. "sync/atomic"
  17. "time"
  18. "unicode"
  19. "tailscale.com/envknob"
  20. "tailscale.com/ipn"
  21. "tailscale.com/ipn/ipnauth"
  22. "tailscale.com/ipn/ipnlocal"
  23. "tailscale.com/ipn/localapi"
  24. "tailscale.com/net/netmon"
  25. "tailscale.com/types/logger"
  26. "tailscale.com/types/logid"
  27. "tailscale.com/util/mak"
  28. "tailscale.com/util/set"
  29. "tailscale.com/util/systemd"
  30. )
  31. // Server is an IPN backend and its set of 0 or more active localhost
  32. // TCP or unix socket connections talking to that backend.
  33. type Server struct {
  34. lb atomic.Pointer[ipnlocal.LocalBackend]
  35. logf logger.Logf
  36. netMon *netmon.Monitor // must be non-nil
  37. backendLogID logid.PublicID
  38. // resetOnZero is whether to call bs.Reset on transition from
  39. // 1->0 active HTTP requests. That is, this is whether the backend is
  40. // being run in "client mode" that requires an active GUI
  41. // connection (such as on Windows by default). Even if this
  42. // is true, the ForceDaemon pref can override this.
  43. resetOnZero bool
  44. startBackendOnce sync.Once
  45. runCalled atomic.Bool
  46. // mu guards the fields that follow.
  47. // lock order: mu, then LocalBackend.mu
  48. mu sync.Mutex
  49. lastUserID ipn.WindowsUserID // tracks last userid; on change, Reset state for paranoia
  50. activeReqs map[*http.Request]*ipnauth.ConnIdentity
  51. backendWaiter waiterSet // of LocalBackend waiters
  52. zeroReqWaiter waiterSet // of blockUntilZeroConnections waiters
  53. }
  54. func (s *Server) mustBackend() *ipnlocal.LocalBackend {
  55. lb := s.lb.Load()
  56. if lb == nil {
  57. panic("unexpected: call to mustBackend in path where SetLocalBackend should've been called")
  58. }
  59. return lb
  60. }
  61. // waiterSet is a set of callers waiting on something. Each item (map value) in
  62. // the set is a func that wakes up that waiter's context. The waiter is responsible
  63. // for removing itself from the set when woken up. The (*waiterSet).add method
  64. // returns a cleanup method which does that removal. The caller than defers that
  65. // cleanup.
  66. //
  67. // TODO(bradfitz): this is a generally useful pattern. Move elsewhere?
  68. type waiterSet set.HandleSet[context.CancelFunc]
  69. // add registers a new waiter in the set.
  70. // It aquires mu to add the waiter, and does so again when cleanup is called to remove it.
  71. // ready is closed when the waiter is ready (or ctx is done).
  72. func (s *waiterSet) add(mu *sync.Mutex, ctx context.Context) (ready <-chan struct{}, cleanup func()) {
  73. ctx, cancel := context.WithCancel(ctx)
  74. hs := (*set.HandleSet[context.CancelFunc])(s) // change method set
  75. mu.Lock()
  76. h := hs.Add(cancel)
  77. mu.Unlock()
  78. return ctx.Done(), func() {
  79. mu.Lock()
  80. delete(*hs, h)
  81. mu.Unlock()
  82. cancel()
  83. }
  84. }
  85. // wakeAll wakes up all waiters in the set.
  86. func (w waiterSet) wakeAll() {
  87. for _, cancel := range w {
  88. cancel() // they'll remove themselves
  89. }
  90. }
  91. func (s *Server) awaitBackend(ctx context.Context) (_ *ipnlocal.LocalBackend, ok bool) {
  92. lb := s.lb.Load()
  93. if lb != nil {
  94. return lb, true
  95. }
  96. ready, cleanup := s.backendWaiter.add(&s.mu, ctx)
  97. defer cleanup()
  98. // Try again, now that we've registered, in case there was a
  99. // race.
  100. lb = s.lb.Load()
  101. if lb != nil {
  102. return lb, true
  103. }
  104. <-ready
  105. lb = s.lb.Load()
  106. return lb, lb != nil
  107. }
  108. // serveServerStatus serves the /server-status endpoint which reports whether
  109. // the LocalBackend is up yet.
  110. // This is primarily for the Windows GUI, because wintun can take awhile to
  111. // come up. See https://github.com/tailscale/tailscale/issues/6522.
  112. func (s *Server) serveServerStatus(w http.ResponseWriter, r *http.Request) {
  113. ctx := r.Context()
  114. w.Header().Set("Content-Type", "application/json")
  115. var res struct {
  116. Error string `json:"error,omitempty"`
  117. }
  118. lb := s.lb.Load()
  119. if lb == nil {
  120. w.WriteHeader(http.StatusServiceUnavailable)
  121. if wait, _ := strconv.ParseBool(r.FormValue("wait")); wait {
  122. w.(http.Flusher).Flush()
  123. lb, _ = s.awaitBackend(ctx)
  124. }
  125. }
  126. if lb == nil {
  127. res.Error = "backend not ready"
  128. }
  129. json.NewEncoder(w).Encode(res)
  130. }
  131. func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) {
  132. ctx := r.Context()
  133. if r.Method == "CONNECT" {
  134. if envknob.GOOS() == "windows" {
  135. // For the GUI client when using an exit node. See docs on handleProxyConnectConn.
  136. s.handleProxyConnectConn(w, r)
  137. } else {
  138. http.Error(w, "bad method for platform", http.StatusMethodNotAllowed)
  139. }
  140. return
  141. }
  142. // Check for this method before the awaitBackend call, as it reports whether
  143. // the backend is available.
  144. if r.Method == "GET" && r.URL.Path == "/server-status" {
  145. s.serveServerStatus(w, r)
  146. return
  147. }
  148. lb, ok := s.awaitBackend(ctx)
  149. if !ok {
  150. // Almost certainly because the context was canceled so the response
  151. // here doesn't really matter. The client is gone.
  152. http.Error(w, "no backend", http.StatusServiceUnavailable)
  153. return
  154. }
  155. var ci *ipnauth.ConnIdentity
  156. switch v := r.Context().Value(connIdentityContextKey{}).(type) {
  157. case *ipnauth.ConnIdentity:
  158. ci = v
  159. case error:
  160. http.Error(w, v.Error(), http.StatusUnauthorized)
  161. return
  162. case nil:
  163. http.Error(w, "internal error: no connIdentityContextKey", http.StatusInternalServerError)
  164. return
  165. }
  166. onDone, err := s.addActiveHTTPRequest(r, ci)
  167. if err != nil {
  168. if ou, ok := err.(inUseOtherUserError); ok && localapi.InUseOtherUserIPNStream(w, r, ou.Unwrap()) {
  169. w.(http.Flusher).Flush()
  170. s.blockWhileIdentityInUse(ctx, ci)
  171. return
  172. }
  173. http.Error(w, err.Error(), http.StatusUnauthorized)
  174. return
  175. }
  176. defer onDone()
  177. if strings.HasPrefix(r.URL.Path, "/localapi/") {
  178. lah := localapi.NewHandler(lb, s.logf, s.netMon, s.backendLogID)
  179. lah.PermitRead, lah.PermitWrite = s.localAPIPermissions(ci)
  180. lah.PermitCert = s.connCanFetchCerts(ci)
  181. lah.ConnIdentity = ci
  182. lah.ServeHTTP(w, r)
  183. return
  184. }
  185. if r.URL.Path != "/" {
  186. http.NotFound(w, r)
  187. return
  188. }
  189. if envknob.GOOS() == "windows" {
  190. // TODO(bradfitz): remove this once we moved to named pipes for LocalAPI
  191. // on Windows. This could then move to all platforms instead at
  192. // 100.100.100.100 or something (quad100 handler in LocalAPI)
  193. s.ServeHTMLStatus(w, r)
  194. return
  195. }
  196. io.WriteString(w, "<html><title>Tailscale</title><body><h1>Tailscale</h1>This is the local Tailscale daemon.\n")
  197. }
  198. // inUseOtherUserError is the error type for when the server is in use
  199. // by a different local user.
  200. type inUseOtherUserError struct{ error }
  201. func (e inUseOtherUserError) Unwrap() error { return e.error }
  202. // checkConnIdentityLocked checks whether the provided identity is
  203. // allowed to connect to the server.
  204. //
  205. // The returned error, when non-nil, will be of type inUseOtherUserError.
  206. //
  207. // s.mu must be held.
  208. func (s *Server) checkConnIdentityLocked(ci *ipnauth.ConnIdentity) error {
  209. // If clients are already connected, verify they're the same user.
  210. // This mostly matters on Windows at the moment.
  211. if len(s.activeReqs) > 0 {
  212. var active *ipnauth.ConnIdentity
  213. for _, active = range s.activeReqs {
  214. break
  215. }
  216. if active != nil {
  217. chkTok, err := ci.WindowsToken()
  218. if err == nil {
  219. defer chkTok.Close()
  220. } else if !errors.Is(err, ipnauth.ErrNotImplemented) {
  221. return err
  222. }
  223. activeTok, err := active.WindowsToken()
  224. if err == nil {
  225. defer activeTok.Close()
  226. } else if !errors.Is(err, ipnauth.ErrNotImplemented) {
  227. return err
  228. }
  229. if chkTok != nil && !chkTok.EqualUIDs(activeTok) {
  230. var b strings.Builder
  231. b.WriteString("Tailscale already in use")
  232. if username, err := activeTok.Username(); err == nil {
  233. fmt.Fprintf(&b, " by %s", username)
  234. }
  235. fmt.Fprintf(&b, ", pid %d", active.Pid())
  236. return inUseOtherUserError{errors.New(b.String())}
  237. }
  238. }
  239. }
  240. if err := s.mustBackend().CheckIPNConnectionAllowed(ci); err != nil {
  241. return inUseOtherUserError{err}
  242. }
  243. return nil
  244. }
  245. // blockWhileIdentityInUse blocks while ci can't connect to the server because
  246. // the server is in use by a different user.
  247. //
  248. // This is primarily used for the Windows GUI, to block until one user's done
  249. // controlling the tailscaled process.
  250. func (s *Server) blockWhileIdentityInUse(ctx context.Context, ci *ipnauth.ConnIdentity) error {
  251. inUse := func() bool {
  252. s.mu.Lock()
  253. defer s.mu.Unlock()
  254. _, ok := s.checkConnIdentityLocked(ci).(inUseOtherUserError)
  255. return ok
  256. }
  257. for inUse() {
  258. // Check whenever the connection count drops down to zero.
  259. ready, cleanup := s.zeroReqWaiter.add(&s.mu, ctx)
  260. <-ready
  261. cleanup()
  262. if err := ctx.Err(); err != nil {
  263. return err
  264. }
  265. }
  266. return nil
  267. }
  268. // localAPIPermissions returns the permissions for the given identity accessing
  269. // the Tailscale local daemon API.
  270. //
  271. // s.mu must not be held.
  272. func (s *Server) localAPIPermissions(ci *ipnauth.ConnIdentity) (read, write bool) {
  273. switch envknob.GOOS() {
  274. case "windows":
  275. s.mu.Lock()
  276. defer s.mu.Unlock()
  277. if s.checkConnIdentityLocked(ci) == nil {
  278. return true, true
  279. }
  280. return false, false
  281. case "js":
  282. return true, true
  283. }
  284. if ci.IsUnixSock() {
  285. return true, !ci.IsReadonlyConn(s.mustBackend().OperatorUserID(), logger.Discard)
  286. }
  287. return false, false
  288. }
  289. // userIDFromString maps from either a numeric user id in string form
  290. // ("998") or username ("caddy") to its string userid ("998").
  291. // It returns the empty string on error.
  292. func userIDFromString(v string) string {
  293. if v == "" || isAllDigit(v) {
  294. return v
  295. }
  296. u, err := user.Lookup(v)
  297. if err != nil {
  298. return ""
  299. }
  300. return u.Uid
  301. }
  302. func isAllDigit(s string) bool {
  303. for i := 0; i < len(s); i++ {
  304. if b := s[i]; b < '0' || b > '9' {
  305. return false
  306. }
  307. }
  308. return true
  309. }
  310. // connCanFetchCerts reports whether ci is allowed to fetch HTTPS
  311. // certs from this server when it wouldn't otherwise be able to.
  312. //
  313. // That is, this reports whether ci should grant additional
  314. // capabilities over what the conn would otherwise be able to do.
  315. //
  316. // For now this only returns true on Unix machines when
  317. // TS_PERMIT_CERT_UID is set the to the userid of the peer
  318. // connection. It's intended to give your non-root webserver access
  319. // (www-data, caddy, nginx, etc) to certs.
  320. func (s *Server) connCanFetchCerts(ci *ipnauth.ConnIdentity) bool {
  321. if ci.IsUnixSock() && ci.Creds() != nil {
  322. connUID, ok := ci.Creds().UserID()
  323. if ok && connUID == userIDFromString(envknob.String("TS_PERMIT_CERT_UID")) {
  324. return true
  325. }
  326. }
  327. return false
  328. }
  329. // addActiveHTTPRequest adds c to the server's list of active HTTP requests.
  330. //
  331. // If the returned error may be of type inUseOtherUserError.
  332. //
  333. // onDone must be called when the HTTP request is done.
  334. func (s *Server) addActiveHTTPRequest(req *http.Request, ci *ipnauth.ConnIdentity) (onDone func(), err error) {
  335. if ci == nil {
  336. return nil, errors.New("internal error: nil connIdentity")
  337. }
  338. lb := s.mustBackend()
  339. // If the connected user changes, reset the backend server state to make
  340. // sure node keys don't leak between users.
  341. var doReset bool
  342. defer func() {
  343. if doReset {
  344. s.logf("identity changed; resetting server")
  345. lb.ResetForClientDisconnect()
  346. }
  347. }()
  348. s.mu.Lock()
  349. defer s.mu.Unlock()
  350. if err := s.checkConnIdentityLocked(ci); err != nil {
  351. return nil, err
  352. }
  353. mak.Set(&s.activeReqs, req, ci)
  354. if len(s.activeReqs) == 1 {
  355. token, err := ci.WindowsToken()
  356. if err != nil {
  357. if !errors.Is(err, ipnauth.ErrNotImplemented) {
  358. s.logf("error obtaining access token: %v", err)
  359. }
  360. } else {
  361. // Tell the LocalBackend about the identity we're now running as.
  362. uid, err := lb.SetCurrentUser(token)
  363. if err != nil {
  364. token.Close()
  365. return nil, err
  366. }
  367. if s.lastUserID != uid {
  368. if s.lastUserID != "" {
  369. doReset = true
  370. }
  371. s.lastUserID = uid
  372. }
  373. }
  374. }
  375. onDone = func() {
  376. s.mu.Lock()
  377. delete(s.activeReqs, req)
  378. remain := len(s.activeReqs)
  379. s.mu.Unlock()
  380. if remain == 0 && s.resetOnZero {
  381. if lb.InServerMode() {
  382. s.logf("client disconnected; staying alive in server mode")
  383. } else {
  384. s.logf("client disconnected; stopping server")
  385. lb.ResetForClientDisconnect()
  386. }
  387. }
  388. // Wake up callers waiting for the server to be idle:
  389. if remain == 0 {
  390. s.mu.Lock()
  391. s.zeroReqWaiter.wakeAll()
  392. s.mu.Unlock()
  393. }
  394. }
  395. return onDone, nil
  396. }
  397. // New returns a new Server.
  398. //
  399. // To start it, use the Server.Run method.
  400. //
  401. // At some point, either before or after Run, the Server's SetLocalBackend
  402. // method must also be called before Server can do anything useful.
  403. func New(logf logger.Logf, logID logid.PublicID, netMon *netmon.Monitor) *Server {
  404. if netMon == nil {
  405. panic("nil netMon")
  406. }
  407. return &Server{
  408. backendLogID: logID,
  409. logf: logf,
  410. netMon: netMon,
  411. resetOnZero: envknob.GOOS() == "windows",
  412. }
  413. }
  414. // SetLocalBackend sets the server's LocalBackend.
  415. //
  416. // If b.Run has already been called, then lb.Start will be called.
  417. // Otherwise Start will be called once Run is called.
  418. func (s *Server) SetLocalBackend(lb *ipnlocal.LocalBackend) {
  419. if lb == nil {
  420. panic("nil LocalBackend")
  421. }
  422. if !s.lb.CompareAndSwap(nil, lb) {
  423. panic("already set")
  424. }
  425. s.startBackendIfNeeded()
  426. s.mu.Lock()
  427. s.backendWaiter.wakeAll()
  428. s.mu.Unlock()
  429. // TODO(bradfitz): send status update to GUI long poller waiter. See
  430. // https://github.com/tailscale/tailscale/issues/6522
  431. }
  432. func (b *Server) startBackendIfNeeded() {
  433. if !b.runCalled.Load() {
  434. return
  435. }
  436. lb := b.lb.Load()
  437. if lb == nil {
  438. return
  439. }
  440. if lb.Prefs().Valid() {
  441. b.startBackendOnce.Do(func() {
  442. lb.Start(ipn.Options{})
  443. })
  444. }
  445. }
  446. // connIdentityContextKey is the http.Request.Context's context.Value key for either an
  447. // *ipnauth.ConnIdentity or an error.
  448. type connIdentityContextKey struct{}
  449. // Run runs the server, accepting connections from ln forever.
  450. //
  451. // If the context is done, the listener is closed. It is also the base context
  452. // of all HTTP requests.
  453. //
  454. // If the Server's LocalBackend has already been set, Run starts it.
  455. // Otherwise, the next call to SetLocalBackend will start it.
  456. func (s *Server) Run(ctx context.Context, ln net.Listener) error {
  457. s.runCalled.Store(true)
  458. defer func() {
  459. if lb := s.lb.Load(); lb != nil {
  460. lb.Shutdown()
  461. }
  462. }()
  463. runDone := make(chan struct{})
  464. defer close(runDone)
  465. // When the context is closed or when we return, whichever is first, close our listener
  466. // and all open connections.
  467. go func() {
  468. select {
  469. case <-ctx.Done():
  470. case <-runDone:
  471. }
  472. ln.Close()
  473. }()
  474. s.startBackendIfNeeded()
  475. systemd.Ready()
  476. hs := &http.Server{
  477. Handler: http.HandlerFunc(s.serveHTTP),
  478. BaseContext: func(_ net.Listener) context.Context { return ctx },
  479. ConnContext: func(ctx context.Context, c net.Conn) context.Context {
  480. ci, err := ipnauth.GetConnIdentity(s.logf, c)
  481. if err != nil {
  482. return context.WithValue(ctx, connIdentityContextKey{}, err)
  483. }
  484. return context.WithValue(ctx, connIdentityContextKey{}, ci)
  485. },
  486. // Localhost connections are cheap; so only do
  487. // keep-alives for a short period of time, as these
  488. // active connections lock the server into only serving
  489. // that user. If the user has this page open, we don't
  490. // want another switching user to be locked out for
  491. // minutes. 5 seconds is enough to let browser hit
  492. // favicon.ico and such.
  493. IdleTimeout: 5 * time.Second,
  494. ErrorLog: logger.StdLogger(logger.WithPrefix(s.logf, "ipnserver: ")),
  495. }
  496. if err := hs.Serve(ln); err != nil {
  497. if err := ctx.Err(); err != nil {
  498. return err
  499. }
  500. return err
  501. }
  502. return nil
  503. }
  504. // ServeHTMLStatus serves an HTML status page at http://localhost:41112/ for
  505. // Windows and via $DEBUG_LISTENER/debug/ipn when tailscaled's --debug flag
  506. // is used to run a debug server.
  507. func (s *Server) ServeHTMLStatus(w http.ResponseWriter, r *http.Request) {
  508. lb := s.lb.Load()
  509. if lb == nil {
  510. http.Error(w, "no LocalBackend", http.StatusServiceUnavailable)
  511. return
  512. }
  513. // As this is only meant for debug, verify there's no DNS name being used to
  514. // access this.
  515. if !strings.HasPrefix(r.Host, "localhost:") && strings.IndexFunc(r.Host, unicode.IsLetter) != -1 {
  516. http.Error(w, "invalid host", http.StatusForbidden)
  517. return
  518. }
  519. w.Header().Set("Content-Security-Policy", `default-src 'none'; frame-ancestors 'none'; script-src 'none'; script-src-elem 'none'; script-src-attr 'none'`)
  520. w.Header().Set("X-Frame-Options", "DENY")
  521. w.Header().Set("X-Content-Type-Options", "nosniff")
  522. w.Header().Set("Content-Type", "text/html; charset=utf-8")
  523. st := lb.Status()
  524. // TODO(bradfitz): add LogID and opts to st?
  525. st.WriteHTML(w)
  526. }